Java内存模型-锁的内存语义

一 引言

  在说volatile的内存语义时,讲过这样一句话:想要理解透volatile特性有一个很好的方法,就是把对volatile变量的单个读/写,当作是使用同一个锁对这些单个读/写操做作了同步。因此其实锁的释放和获取与volatile的写和读具备相同的内存语义。html

二 锁的释放-获取创建的happens-before关系

  不清楚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

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

四 锁内存语义的实现

  锁有不少种,但其基本原理都是差很少的。书上是以ReentrantLock中的公平锁与非公平锁做为案例分析,有兴趣的同窗能够去阅读原籍和源码。现总结以下:

  • 公平锁和非公平锁释放时,最后都要写一个volatile变量state。
  • 公平锁获取是,首先会去读volatile变量。
  • 非公平锁获取时,首先会用CAS更新volatile变量,这个操做同时具备volatile读和写的内存语义。

  因此锁释放-获取的内存语义的实现至少有下面两种方式:

  1)利用volatile变量的写-读所具备的内存语义。

  2)利用CAS所附带的volatile读和volatile写的内存语义。

  由此可知:并发包下的类的实现方式大部分都是基于这两种方式实现的。

相关文章
相关标签/搜索