在说volatile的内存语义时,讲过这样一句话:想要理解透volatile特性有一个很好的方法,就是把对volatile变量的单个读/写,当作是使用同一个锁对这些单个读/写操做作了同步。因此其实锁的释放和获取与volatile的写和读具备相同的内存语义。html
不清楚happens-before规则的请前去看-看,这里就不在细说了。因为在以前没有举例说明监视器锁规则,那么在这里就详细说明下,下面是锁释放-获取的示例代码:并发
public class MonitorExample { int a = 0; public synchronized void writer() { // 1 a += 1; // 2 } // 3 public synchronized void reader() { // 4 System.out.println(a); // 5 } // 6 public static void main(String[] args) { final MonitorExample me = new MonitorExample(); Thread t1 = new Thread(new Runnable() { @Override public void run() { me.writer(); } }); t1.start(); Thread t2 = new Thread(new Runnable() { @Override public void run() { me.reader(); } }); t2.start(); } }
这里咱们假设线程1先执行writer()方法,随后线程B执行reader()方法(知道为何要假设?由于不是必定按这种顺序发生,能够测试下结果)。根据happens-before规则,这个过程包含的happens-before关系能够分为3类:app
1) 根据程序次序规则:1 happens-before 2, 2 happens-before 3, 4 happens-before 5, 5 happens-before 6;ide
2)根据监视器锁规则:3 happens-before 4;测试
3)根据传递性规则,2 happens-before 5。spa
上述happens-before关系的图形化表现形式以下:线程
当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。以上面的MonitorExample程序为例,线程1释放锁后,共享数据的状态示意图以下:code
当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内存中读取共享变量。htm
对比锁释放-读取的内存语义与volatile写-读的内存语义能够看出,锁释放与volatile写具备相同的内存语义;锁获取与volatile读具备相同的内存语义。下面对锁释放和锁获取的内存语义作个总结。blog
锁有不少种,但其基本原理都是差很少的。书上是以ReentrantLock中的公平锁与非公平锁做为案例分析,有兴趣的同窗能够去阅读原籍和源码。现总结以下:
因此锁释放-获取的内存语义的实现至少有下面两种方式:
1)利用volatile变量的写-读所具备的内存语义。
2)利用CAS所附带的volatile读和volatile写的内存语义。
由此可知:并发包下的类的实现方式大部分都是基于这两种方式实现的。