Java中的volatile关键字最全总结(一)
变量的不可见问题
来看一个简单的案例:
public class VolatileDemo {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
while (true) {
if(myThread.isTag()){
System.out.println("----------");
}
}
}
}
@Data
class MyThread extends Thread {
private boolean tag;
@Override
public void run() {
tag = true;
System.out.println("子线程中tag为:" + tag);
}
}
这段程序应该非常好理解,当子线程被创建并调用时,子线程会执行run()方法将tag的属性值修改为true,修改完成后main方法中的while循环条件就成立了,所以程序的运行结果应该是:
子线程中tag为:true
----------
但事实上,运行结果是这样的:
image.png
那么很明显,main方法并没有得到子线程修改后的tag值,这一现象就是多线程下变量的不可见问题。
那么main方法为何得不到子线程修改后的tag值呢?我们需要来了解一下JMM(Java内存模型)。在JMM中,所有的共享变量都会被保存到主存中,共享变量指的是实例变量和成员变量,局部变量不属于共享变量,他是线程私有的;当启动某个线程时,会开辟一个独立的内存空间提供线程使用,并从主存中拷贝一个共享变量的副本存入线程的内存里,线程只能对该变量的副本进行读写操作,不能直接操作主存中的共享变量,不同的线程之间也无法相互访问独立空间中的变量,而是需要通过主存进行数据的传递,如下图所示:
image.png
由此,我们可以分析刚才的程序:
image.png
首先主存中有一个共享变量tag,值为false,当子线程启动时,便会拷贝一份tag的副本存放于子线程的内存空间中,然而在子线程将tag值修改并写回主存之前,main线程也从主存中拷贝了一个tag的副本,所以此时main线程中tag的值仍然为false,这导致main线程中的if条件不成立,又因为while(true)底层的一些原因,使得它的执行效率非常地高,使得main线程无法再去主存中重新读取tag的值,即:取的一直都是main线程中的变量副本,这也就解释了为什么会出现变量不可见的问题了。
文章转自公众号:三友的java日记