JUC 源码分析 二 ReentrantLock

上一篇文章已完整展示了等待队列的管理(添加结点、移除取消结点)、独占模式下的acquire操作、acquire中断取消、前驱如何通知后继。这些知识已足够用来实现一个可重入锁。

本篇通过java.util.concurrent.locks.ReentrantLock类的源码来分析如何实现可重入锁。

可重入锁

可重入锁就是说当线程拥有这把锁的时候,它再次请求锁是成功的;当线程释放锁时,如果持有锁的线程对锁的请求次数大于释放次数,则该线程仍然拥有锁,直到请求次数与释放次数相等时才真正释放锁。

所以可重入锁需要一个重入计数变量,初始值设为0,当成功请求锁时加1,释放锁时减1,当释放锁之后计数为0则真正释放锁。重入锁还必须持有对锁持有者的引用,用以判断是否可以重入。

锁的公平性

如果锁能够严格按照线程请求锁的先后顺序分配锁,则认为锁具有公平性;如果某一线程能在其他等待线程之前获取到锁,则认为锁不具有公平性。

ReentrantLock

ReentrantLock是JUC包里可重入的独占锁实现,它具有三个内部类:Sync、NonfairSync、FairSync,通过构造函数的参数来指定锁是否是公平的,下面是一些核心代码:

public class ReentrantLock implements Lock, java.io.Serializable {
     private final Sync sync;

     public ReentrantLock(boolean fair) {
         sync = fair ? new FairSync() : new NonfairSync();
     }

     public void lock() {
         sync.lock();
     }

     public void unlock() {
         sync.release(1);     // 这个1表示退出锁1次。
     }

     // 带超时限制的获取
  public boolean tryLock(long timeout, TimeUnit unit)
          throws InterruptedException {
      return sync.tryAcquireNanos(1, unit.toNanos(timeout));
  }

     // 其他代码省略
}

可以看到,ReentrantLock都是把具体实现委托给内部类而不是直接继承自AbstractQueuedSynchronizer,这样的好处是用户不会看到不需要的方法,也避免了用户错误地使用AbstractQueuedSynchronizer的公开方法而导致错误。

ReentrantLock的重入计数是使用AbstractQueuedSynchronizerstate属性的,state大于0表示锁被占用、等于0表示空闲,小于0则是重入次数太多导致溢出了。
继续阅读