可重入锁

可重入锁表示如果你已经获取了锁,如果你想再次获取该锁,依然能够正常获取到,不会因为之前已经获取过还没释放而阻塞。synchronized关键字是隐式的可重入锁,表现在synchronized修饰的同步方法内递归调用自己不会阻塞。jdk中实现Lock接口的可重入锁ReentrantLock是如何实现可重入的呢?在阅读这篇文章之前,我假设你已经读过Lock内的同步器,对锁的实现有一定的了解。
可重入锁实现要点

  • 获取锁后,再次获取该锁时;判断当前线程是否是已经获取锁的线程,若是,则通过;否则,阻塞。
  • 释放锁时,如果该线程获取了n(n>=1)次锁,则需要释放n次,才能释放成功,返回true;否则,返回false。
代码实现
  • 获取可重入锁(非公平锁)同步状态的局部代码如下:
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) {//此时,锁未被获取 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } //当前线程就是此时占有锁的线程 else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }

【可重入锁】代码中发现,如果当前线程就是已经占有锁的线程,则同步状态值进行增加,并返回true。
  • 释放可重入锁(非公平锁)的局部代码如下:
protected final boolean tryRelease(int releases) { int c = getState() - releases; //当前线程不是占有锁的线程 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; //同步状态值为初始值0时,返回true;否则,返回false }

代码中发现同步状态值0是锁是否成功释放的标志。
  • 获取可重入锁(公平锁)的局部代码如下:
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { //当前节点是否有前驱节点 if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }

与非公平锁获取同步状态相比,唯一的区别是判断引用当前线程的当前节点是否有前驱节点,如果返回true,说明有更早的节点在等待获取锁,则当前节点需要等到前驱节点获取并释放锁后才能够继续获取锁。至于释放锁,与非公平锁并无区别。
参考
  1. 并发编程的艺术

    推荐阅读