A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 李江 中级黑马   /  2013-10-8 08:21  /  1285 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

ReentrantLock分为公平锁和非公平锁,我们首先分析公平锁。

使用公平锁时,加锁方法lock()的方法调用轨迹如下:

  • ReentrantLock : lock()
  • FairSync : lock()
  • AbstractQueuedSynchronizer : acquire(int arg)
  • ReentrantLock : tryAcquire(int acquires)

在第4步真正开始加锁,下面是该方法的源代码:

  1. protected final boolean tryAcquire(int acquires) {
  2.     final Thread current = Thread.currentThread();
  3.     int c = getState();   //获取锁的开始,首先读volatile变量state
  4.     if (c == 0) {
  5.         if (isFirst(current) &&
  6.             compareAndSetState(0, acquires)) {
  7.             setExclusiveOwnerThread(current);
  8.             return true;
  9.         }
  10.     }
  11.     else if (current == getExclusiveOwnerThread()) {
  12.         int nextc = c + acquires;
  13.         if (nextc < 0)  
  14.             throw new Error("Maximum lock count exceeded");
  15.         setState(nextc);
  16.         return true;
  17.     }
  18.     return false;
  19. }
复制代码

从上面源代码中我们可以看出,加锁方法首先读volatile变量state。

在使用公平锁时,解锁方法unlock()的方法调用轨迹如下:

  • ReentrantLock : unlock()
  • AbstractQueuedSynchronizer : release(int arg)
  • Sync : tryRelease(int releases)

在第3步真正开始释放锁,下面是该方法的源代码:

  1. protected final boolean tryRelease(int releases) {
  2.     int c = getState() - releases;
  3.     if (Thread.currentThread() != getExclusiveOwnerThread())
  4.         throw new IllegalMonitorStateException();
  5.     boolean free = false;
  6.     if (c == 0) {
  7.         free = true;
  8.         setExclusiveOwnerThread(null);
  9.     }
  10.     setState(c);           //释放锁的最后,写volatile变量state
  11.     return free;
  12. }
复制代码
从上面的源代码我们可以看出,在释放锁的最后写volatile变量state。公平锁在释放锁的最后写volatile变量state;在获取锁时首先读这个volatile变量。根据volatile的happens-before规则,释放锁的线程在写volatile变量之前可见的共享变量,在获取锁的线程读取同一个volatile变量后将立即变的对获取锁的线程可见。现在我们分析非公平锁的内存语义的实现。非公平锁的释放和公平锁完全一样,所以这里仅仅分析非公平锁的获取。使用公平锁时,加锁方法lock()的方法调用轨迹如下:
  • ReentrantLock : lock()
  • NonfairSync : lock()
  • AbstractQueuedSynchronizer : compareAndSetState(int expect, int update)
在第3步真正开始加锁,下面是该方法的源代码:
  1. protected final boolean compareAndSetState(int expect, int update) {
  2.     return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
  3. }
复制代码
该方法以原子操作的方式更新state变量,本文把java的compareAndSet()方法调用简称为CAS。JDK文档对该方法的说明如下:如果当前状态值等于预期值,则以原子方式将同步状态设置为给定的更新值。此操作具有 volatile 读和写的内存语义。这里我们分别从编译器和处理器的角度来分析,CAS如何同时具有volatile读和volatile写的内存语义。前文我们提到过,编译器不会对volatile读与volatile读后面的任意内存操作重排序;编译器不会对volatile写与volatile写前面的任意内存操作重排序。组合这两个条件,意味着为了同时实现volatile读和volatile写的内存语义,编译器不能对CAS与CAS前面和后面的任意内存操作重排序。下面我们来分析在常见的intel x86处理器中,CAS是如何同时具有volatile读和volatile写的内存语义的。


0 个回复

您需要登录后才可以回帖 登录 | 加入黑马