volatile可用来解决编译器优化代码时带来的多线程不安全问题
问题一: 内存可见性问题
(当有多个线程时,其中一个线程将代码改了, 单另一个线程并没有及时感知到)
造成这种问题的原因是当编译器优化多线程代码时可能会导致读取内存数据时不去主内存即内存(main memory)中读取而是直接去工作内存即CPU寄存器(work memory)中直接取用. 一下是可能引发这种情况的案例
class Counter {
public int count = 0;
public void Setcount(int a){
count = a;
}
}
public class Sep15thdemo2 {
static Counter counter = new Counter();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while(counter.count == 0){//多次读取与比较
}
});
Thread t2 = new Thread(() -> {
counter.Setcount(1);//将变量count的值修改为1
});
t1.start();//启动线程t1
Thread.sleep(500);//等待线程ti中进行一段时间的对count的读取
t2.start();//启动线程t2
t2.join();//等待t2线程中代码执行完毕再往下执行
System.out.println(counter.count);
}
}
上述问题主要是当进行多次读取操作时发现count的值未发生改变,编译器就不在去主内存中读取数据.而是直接从工作内存中取数据来语0进行比较,这样省去了读取内存这种开销较大的操作提升了效率,但是当线程t2启动时将内存中count的值由0改为了1,按照我们的预期应该是线程t1中while循环的判断条件由true变为了false应该跳出循环结束进程.但是其结果确实while的循环条件依旧为true程序陷入了循环不会结束.这就是内存可见性所造成的线程不安全
上述情况的解决方法就是利用关键字volatile来阻止编译器对代码的优化,也就避免了内存可见性问题, 使用方法如下
class Counter {
volatile public int count = 0;//再此处加上volatile关键字就可以防止内存可见性问题
public void Setcount(int a){
count = a;
}
}
public class Sep15thdemo2 {
static Counter counter = new Counter();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while(counter.count == 0){//多次读取与比较
}
});
Thread t2 = new Thread(() -> {
counter.Setcount(1);//将变量count的值修改为1
});
t1.start();//启动线程t1
Thread.sleep(500);//等待线程ti中进行一段时间的对count的读取
t2.start();//启动线程t2
t2.join();//等待t2线程中代码执行完毕再往下执行
System.out.println(counter.count);
}
}