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

  

Class MonitorEx{
int a = 0 ;
public synchronized void writer(){ //1
a++ ; //2 }                   //3 public synchronized void reader(){ //4 int i = a ; //5
......
} //6 }

  假设线程A执行writer()方法,随后线程B执行reader()方法。根据happens-before规则,这个过程包含的happens-before关系有如下3类 :

  (1)根据程序次序规则 : 1 happens-before 2 , 2 happens-before 3 , 4 happens-before 5 , 5 happens-before 6 ;

  (2)根据监视器规则 : 3 happens-before 4 ;

  (3)根据传递依赖规则 : 2 happens-before 5 ;

  happens-before 关系可以转换为如下图的表现形式 :

  Java中锁的内存语义-LMLPHP

  在上图中,每一个箭头连接的两个点,代表了一个happens-before 关系,短箭头表示的是程序的顺序规则 ; 红色箭头表示监视器规则;黑色长箭头表示的是组合这些规则后提供的happens-before保证 。  

  上图表示线程A在释放了锁之后,随后线程B获取同一个锁,2 happens-before 5 。因此,线程A在释放锁之前所有的可见的共享变量,在B线程获取同一个锁之后,将立刻变得对线程B可见。

  

  以上的介绍是对我们程序员可见的,那么锁的释放和获取在内存中的语义是怎么样的呢 ?

  当线程释放锁时,Java内存模型(Java Memory Modle 简称JMM) 会把该线程对应的本地内存中的共享变量刷新到主内存中,以上面的例子为例,当线程A释放锁后,共享数据的状态示意图如下图所示 :

Java中锁的内存语义-LMLPHP

  当线程获取锁时,JMM会把该线程对应的本地内存置为无效,从而使得被监视器保护的临界区代码必须从主内存中读取共享变量。下图表示锁获取的状态示意图 :

Java中锁的内存语义-LMLPHP

  锁的释放和锁的获取的内存语义可以总结如下:

  □ 线程A释放一个锁,实质上是线程A向接下来要获取同一个锁的某个线程发出了(线程A对共享变量做出修改)消息 。

  □ 线程B获取一个锁,实质上是线程B接收了之前某个线程发出的(在释放这个锁之前对共享变量所做修改)的消息。

  □ 线程A释放锁,随后线程B获取这个锁,这个过程实质上是线程A通过主内存向线程B发送消息 。

  

  

     

  

  

  

05-11 15:23