volatile 关键字
volatile主要有两种作用,一个是保证其可见性,另一个则是保证其顺序性。
可见性:多个线程共享一个变量,每个线程都会将这个共享变量存放一份到自己的变量区,当一个线程修改了这个共享变量,它首先会修改到自己的变量区,可能并不会立即修改到公共的内存区。如果加上volatile这个修饰,则当值修改的时候,会立即同步到公共内存区,同时每个线程的变量区都将失效,重新获取公共内存区里面的值。
顺序性:在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性,而volatile就确保了顺序不被打乱。
可见性的具体实例,用一个标志位来停止另一个线程的运行。具体代码如下:
public class Test {
private volatile static boolean isStop = false;
public static void main(String[] arg0){
Thread thread = new Thread(new Test().new MyThread());
thread.start();
isStop = true;
}
class MyThread implements Runnable{
@Override
public void run() {
while (!isStop){
}
}
}
}
简单的解释一下以上代码。
上面一共两个线程,一个是main一个是MyThread,他们共享一个变量 isStop,所以在main线程和MyThread都会有一个变量区,当我们修改了main线程的 isStop,它的变量区会被修改,共享内存区域里面就不一定修改(虽然概率及其的小),但是如果我们加上volatile修饰,isStop修改之后会立即同步到公共内存区,MyThread里面的变量区失效,重新获取公共区的isStop。
顺序性的具体实例,就举网上通用的例子,单例模式。
public class SingleInastace {
private volatile static SingleInastace singleInastace = null;
private SingleInastace(){
}
public static SingleInastace getSingleInastace(){
if (singleInastace != null){
synchronized(SingleInastace.class){
if (singleInastace != null){
singleInastace = new SingleInastace();
}
}
}
return singleInastace;
}
}
从以上代码看出,有可能发生顺序变换的 singleInstance = new SingleInstance();这句主要有三步操作。
1)给singleInstance 分配内存(栈空间)。
2)初始化SingleInstance()的构造函数。
3) singleInstance 对象指向堆空间(此时singleinstance)
当然正常的顺序是 1–>2–>3,也可能是 1–>3–>2,因为单线程的情况下运行的结果是一样的。但是如果多线程的情况下仍然按照 1–>3–>2的流程走,就会出问题。