堆中对象是线程共享的,为什么还需要用volatile来修饰对象?

Posted zhangjin1120

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了堆中对象是线程共享的,为什么还需要用volatile来修饰对象?相关的知识,希望对你有一定的参考价值。

 volatile有两个作用,一是禁止指令重排序,二个是保证共享变量对所有线程的内存可见性。禁止指令重排序,在单例模式中防止出现空指针异常。对于保证内存可见性,堆中对象明明是线程共享的,为什么还需要用volatile来修饰对象?
  多线程对共享变量的修改分为很多步骤。举个简单的例子:

public class VolatileTest {
        private static  boolean flag = true;
        public static void main(String[] args) throws InterruptedException {
            Thread threadA = new Thread(() -> {
                while (flag){
                    //注意在这里不能有输出
                };
                System.out.println("threadA over");
            });
            threadA.start();
            //休眠100毫秒,让线程A先执行
            Thread.sleep(100);
            //主线程设置共享变量flag等于false
            flag = false;
            System.out.println("主线程执行完毕");
        }
}

输出:主线程执行完毕,线程A一直死循环。
添加volatile后,输出是:

主线程执行完毕
threadA over
Process finished with exit code 0

 死循环产生的原因:主线程把自己工作内存的flag值设置成false后,同步到主内存,此时主内存flag=false。线程A并没有读取到主内存最新的flag值(false),线程A工作内存一直占着cpu时间片,不会从主内存更新最新的flag值,线程A看不到主内存最新值,A线程使用的值和主线程使用值不一致。
 简单的讲,main线程修改共享变量的值后,没有及时同步到A线程。

volatile是如何解决这个问题的?

volatile实现内存可见性是通过store和load指令完成的;也就是对volatile变量执行写操作时,会在写操作后加入一条store指令,即强迫线程将最新的值刷新到主内存中;而在读操作时,会加入一条load指令,即强迫从主内存中读入变量的值。

有没有急需一张java内存分布图?

volatile,synchronized可见性,有序性,原子性代码证明(基础硬核)
【Java多线程】内存模型JMM—主内存与工作内存分析
JVM内存分配机制之栈上分配与TLAB的区别
Java堆内存是线程共享的!面试官:你确定吗?
Java内存模型之可见性分析
volitale 怎么保证可见性

以上是关于堆中对象是线程共享的,为什么还需要用volatile来修饰对象?的主要内容,如果未能解决你的问题,请参考以下文章

java中级面试题

2020 Java 面试题 小结 (答案慢慢补上,有错误请指出)

volatile为啥不能保证原子性

java之用volatile和不用volatile的区别

java——同步机制(synchronized, volatile)

多线程中的volatile和伪共享