int a; boolean flag; public synchronized void init(){// ① a = 100; // ② flag = true; // ③ }// ④ public synchronized void doTask(){ // ⑤ if(flag){ // ⑥ int result = a; // ⑦ } } // ⑧
假设线程A执行init(),线程B执行doTask(),有以下的happens-before关系:java
根据传递规则,保证init()方法全部的修改对doTask()方法可见,happens-before关系以下所示
缓存
一、2间的表示程序次序性规则,四、5间的表示监视器规则,因为三、4有happens-before关系,四、5有happens-before关系,因此根据传递性规则,二、6间有happens-before关系。数据结构
线程A释放锁以前全部可见的共享变量,在线程B获取同个锁以后就变得可见了。app
当释放锁时,JMM会把线程对应的本地内存中的共享变量刷新到主内存。以上面的例子为例,共享数据的状态示意图以下所示
学习
当A线程获取到锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内存中读取共享变量。线程
对比锁释放-获取的内存语义和volatile的写-读内存语义能够看出:锁释放与volatile写有相同的内存语义;锁获取和volatile读有相同的内存语义。3d
这里判断语义是否相同是经过两个操做的流程是否相同,好比线程A的锁释放完时,刷新至主内存;volatile写完后,刷新至主内存,并通知其余线程本地内存的共享变量失效(在锁释放环节里是交给锁获取执行);code
synchronized是经过控制对象头来控制锁的升级,可是具体获取锁和释放锁的流程藏在JVM里,这里将经过ReentrantLock类比synchronized过程。对象
这里要学习的是CAS,JDK文档对该方法的说明以下:若是当前状态值等于预期值,则以原子方式将同步设置为给定的更新值。此操做具备volatile读和写的语义。
前面讲到volatile写保证volatile写不会和前面的操做发生重排序,volatile读保证volatile读不会和后面的操做发生重排序。组合这两个条件就意味着同时实现了 禁止某一操做和操做前、操做后的重排序。CAS操做就是如此,它在是经过加上lock前缀来实现如下的功能:
正是由于CAS同时具备volatile读和写的内存语义,所以Java线程之间的通讯有下面四种方式。
JUC包的通用化的实现模式:
AQS,非阻塞数据结构和原子变量类,这些JUC包中的基础都是使用上面的模式来实现的,而JUC包的高层类又是依赖这些基础类来实现的。从总体看,JUC包的实现示意图以下所示。