Java内存模型系列简介

 2019-12-10 16:17  阅读(1229)
文章分类:Java Core

JMM

Java内存模型的抽象示意图:

20191210001715\_1.png

Java线程间消息通信:

20191210001715\_2.png

线程A将x=1传递给线程B,经历过程如下:

  • 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。
  • 然后,线程B到主内存中去读取线程A之前已更新过的共享变量。

至于如何保证线程间数据的准确性,则需要利用同步锁来配合。

重排序

从java源代码到最终实际执行的指令序列,会分别经历下面三种重排序:

20191210001715\_3.png

重排序重点理解以下原则:

  • 数据依赖性原则
  • as-if-serial语义
  • happens-before
  • 控制依赖关系
  • 顺序一致性

重排序带来的问题就是程序执行结果会被改变。因此需要各种约定、规范来限制部分编译器、处理器重排序。

数据依赖性原则:对于两个操作A、B存在依赖关系的,编译器、处理器不会改变其执行顺序。
as-if-serial语义:对于单线程程序,无论怎么重排序,程序结果不能改变。
happens-before:前一操作结果对后一操作可见;前一操作按顺序排在后一操作前。
控制依赖关系:if、while等控制依赖,多线程中依旧会重排序。
顺序一致性:理想的内存模型,线程内操作必须有序。

锁是java并发编程中最重要的同步机制。锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息。下面是锁释放-获取的示例代码:

class MonitorExample {
        int a = 0;

        public synchronized void writer() {  //1
            a++;                             //2
        }                                    //3

        public synchronized void reader() {  //4
            int i = a;                       //5
            ……
        }                                    //6
    }

20191210001715\_4.png

下面对锁释放和锁获取的内存语义做个总结:

  • 线程A释放一个锁,实质上是线程A向接下来将要获取这个锁的某个线程发出了(线程A对共享变量所做修改的)消息。
  • 线程B获取一个锁,实质上是线程B接收了之前某个线程发出的(在释放这个锁之前对共享变量所做修改的)消息。
  • 线程A释放锁,随后线程B获取这个锁,这个过程实质上是线程A通过主内存向线程B发送消息。

final域

写final域的重排序规则

20191210001715\_5.png

写final域的重排序规则可以确保:在对象引用为任意线程可见之前,对象的final域已经被正确初始化过了。

读final域的重排序规则

20191210001715\_6.png

读final域的重排序规则可以确保:在读一个对象的final域之前,一定会先读包含这个final域的对象的引用。

参考

Java内存模型系列资料http://ifeve.com/java-memory-model-0/

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> Java内存模型系列简介

相关推荐