Java中的volatile关键字最全总结(一)

pivoteic
发布于 2022-6-15 17:06
浏览
0收藏

 

变量的不可见问题

来看一个简单的案例:

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
----------


但事实上,运行结果是这样的:

Java中的volatile关键字最全总结(一)-鸿蒙开发者社区

 image.png

那么很明显,main方法并没有得到子线程修改后的tag值,这一现象就是多线程下变量的不可见问题。

 

那么main方法为何得不到子线程修改后的tag值呢?我们需要来了解一下JMM(Java内存模型)。在JMM中,所有的共享变量都会被保存到主存中,共享变量指的是实例变量和成员变量,局部变量不属于共享变量,他是线程私有的;当启动某个线程时,会开辟一个独立的内存空间提供线程使用,并从主存中拷贝一个共享变量的副本存入线程的内存里,线程只能对该变量的副本进行读写操作,不能直接操作主存中的共享变量,不同的线程之间也无法相互访问独立空间中的变量,而是需要通过主存进行数据的传递,如下图所示:

Java中的volatile关键字最全总结(一)-鸿蒙开发者社区

 image.png

由此,我们可以分析刚才的程序:

Java中的volatile关键字最全总结(一)-鸿蒙开发者社区

 image.png

首先主存中有一个共享变量tag,值为false,当子线程启动时,便会拷贝一份tag的副本存放于子线程的内存空间中,然而在子线程将tag值修改并写回主存之前,main线程也从主存中拷贝了一个tag的副本,所以此时main线程中tag的值仍然为false,这导致main线程中的if条件不成立,又因为while(true)底层的一些原因,使得它的执行效率非常地高,使得main线程无法再去主存中重新读取tag的值,即:取的一直都是main线程中的变量副本,这也就解释了为什么会出现变量不可见的问题了。

 

文章转自公众号:三友的java日记

标签
已于2022-6-15 17:06:13修改
收藏
回复
举报
回复
    相关推荐