java内存可见性

来源:互联网 发布:多益网络官网账号注册 编辑:程序博客网 时间:2024/04/29 20:30
        理解java内存可见性,首先我们要理解java内存模型。什么是java内存模型(JMM)?
        Java Memory Model (JAVA 内存模型)描述线程之间如何通过内存(memory)来进行交互。 具体说来, JVM中存在一个主存区(Main Memory或Java Heap Memory),对于所有线程进行共享,而每个线程又有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作并非发生
在主存区,而是发生在工作内存中,而线程之间是不能直接相互访问,变量在程序中的传递,是依赖主存来完成的。
        java内存模型如下图所示:

        共享变量可见性实现原理图下图所示:

        主要有一下两个步骤:
                1,把工作内存1中更新过的共享变量刷新到主内存中
                2,将主内存中最新的共享变量的值更新到工作内存中
        两条规定:
                1,线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写
                2,不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量的值的传递需要通过主内存来完成。
        导致共享变量在线程间不可见的原因:
                1,线程的交叉执行 -->原子性
                2,重排序接合线程交叉执行-->原子性
                3,共享变量未及时更新 -->可见性
        我们先了解下重排序:
                重排序是指:代码书写的顺序与实际执行的顺序不一致,指令重排序是编译器或处理器为了提高程序性能而做的优化
                1,编译器优化的重排序(编译器优化)
                2,指令级并行重排序(处理器优化)(多核)
                3,内存系统的重排序(处理器优化)
                如下图:

        as-if-serial是使重排序的结果和代码的书写顺序一致:重排序不会给单线程带来内存可见性问题。多线程中程序交错执行时,重排序可能会造成内存可见性问题。

synchronzied实现可见性
        synchronzied能够实现:
                原子性(同步)、可见性
                JMM关于synchronzied的两条规定:
                        线程解锁前,必须把共享变量的最新值更新到主内存中
                        线程加锁时,将清除工作内存中的共享变量值,从而使共享变量时需要从主内存中重新读取最新的值(注意加锁和解锁需要同一把锁)
                线程解锁前对共享变量的修改在下次加锁时对其他线程可见。
                线程执行互斥代码的过程:
                1.获得互斥锁
                2.清空工作内存
                3.从主内存拷贝变量的最新副本到工作内存
                4.执行代码
                5.将更改后的共享变量的值刷新到主内存中
                6.释放互斥锁
volatile实现可见性
        volatail关键字能够保证volatile变量的可见性,但是不能保证volatile变量复合操作的原子性
        volatile如何实现内存可见性:
                通过加入内存屏障和禁止重排序优化来实现的。
                1,对volatile变量执行写操作时,会在写操作后加入一条store屏障指令
                2,对volatile变量执行读操作时,会在读操作前加入一条load屏障指令
                也就是说,volatile变量在每次被线程访问,都强迫从主内存中读取改变量的值,而当该变量的值发生变化时,又会强迫线程将最新的值刷新到主>内存中。这样任何时刻,不同的线程总能看到改变量的最新值。
                线程写volatile变量的过程:
                        1,改变线程工作内存中volatile变量副本的值
                        2,将改变后的副本的值从工作内存刷新到主内存中
                线程读volatile变量的过程:
                        1,从主内存中读取volatile变量的罪行值到线程的工作内存中
                        2,从工作内存中读取volatile变量的副本
        volatile部保证volatile变量复合操作的原子性:
                例如:
                        private int number=0;
                        number++;不是原子操作
                        1,读取number的值
                        2,将number的值加1
                        3,写入最新的number的值
                如果synchronzied(this){
                                number++;
                        }
                可以变为原子操作
                但是private volatile int number=0;
                不可以变为原子操作


0 0
原创粉丝点击