1. 回顾Java内存模型
在上一篇中详细介绍了Java内存模型及happens-before原则,如果还没有阅读过该文章的读者建议先阅读上一篇文章,对本文的掌握会有很大的帮助:Java并发篇(2)解读JMM内存模型
1.1 主内存与工作内存
在上一篇文章中我们已经知道线程是通过主内存去进行线程间的隐式通信的,而线程对共享变量的写操作在工作内存中完成,由JMM控制共享变量由工作内存写回到主内存的时机。
JMM提供了一个保证内存可见性的原则:happens-before原则。这个原则可以保证线程对共享变量的写操作对其它线程可见。如果在多线程环境下需要满足happens-before原则,就必须对共享变量添加某种特定的读写规则,否则会导致多线程环境下对共享变量的操作无法对其它线程可见,造成缓存不一致的现象。
彻底掌握volatile关键字/20200424085645.png)
1.2 如何保证操作可见性
如下图所示,在JMM中定义了8种原子操作保证线程的操作具有内存可见性。
lock - read - load - use 表示一个线程需要读取并使用共享变量的过程。
assign - store - write - unlock 表示一个线程需要写入共享变量的过程。
这两个读-写操作都是内存可见的,如果破坏了这些顺序,那么就无法保证内存可见性。关于这8个原子操作,不展开细说,枯燥的概念会让人打瞌睡,我们可以这样理解:
- 线程读共享变量时必须获取变量的锁,从主内存中获取共享变量的值并加载到工作内存中使用;
- 线程写共享变量时必须事先获取共享变量的锁,更新工作内存中的共享变量值后立即写入到主内存,然后释放该变量的锁。
我们不需要记住这8种原子操作的顺序,只要满足happens-before原则,就必定满足上图的情况。换言之,我们可以通过happens-before原则判断内存访问操作是否是线程间可见。