JAVA里的锁有两大类,一类是synchronized锁,一类是concurrent包里的锁(JUC锁)。其中synchronized锁是JAVA语言层面提供的能力,本文主要讨论JUC里的ReentrantLock锁。
JDK层
AbstractQueuedSynchronizer(AQS)
ReentrantLock的lock()、unlock()等API其实依赖于内部的Synchronizer(注意不是synchronized)来实现。Synchronizer又分为FairSync和NonfairSync,顾名思义即公平和非公平。
当调用ReentrantLock的lock()方法时,其实就只是简单地转交给Synchronizer的lock()方法。
ReentrantLock的主要方法和结构如下:
1 | public class ReentrantLock implements Lock, java.io.Serializable { |
我们看到Sync 继承自AbstractQueueSynchronizer(AQS),AQS是JUC包的基石,AQS本身并不实现任何同步接口(比如lock、unlock、countDown等等),但是它定义了一个并发资源控制逻辑的框架(运用了template method设计模式),它定义了acquire()和release()方法用于独占地(exclusive)获取和释放资源,以及acquireShared()和releaseShared()方法用于共享地获取和释放资源。比如acquire()/release()用于实现ReentrantLock,而acquireShared/releaseShared用于实现CountDownLacth、Semaphore。
AQS.acquire()定义如下:
1 | public final void acquire(int arg) { |
整体逻辑是,先进行一次tryAcquire(),如果成功了,调用者继续执行自己后面的代码;如果失败,则执行addWaiter()和acquireQueued()()。其中tryAcquire()需要子类根据自己的同步需求进行实现,而acquireQueued() 和addWaiter() 已经由AQS实现。addWaiter()的作用是把当前线程加入到AQS内部同步队列的尾部,而acquireQueued()的作用是当tryAcquire()失败的时候阻塞当前线程。
addWaiter()的代码如下:
1 | private Node addWaiter(Node mode) { |
enq(node)的代码如下:
1 | private Node enq(final Node node) { |
- 同步队列的结构如下:

acquireQueued()的代码如下:
1 | final boolean acquireQueued(final Node node, int arg) { |
判断自己是不是同步队列中的第一个排队的节点,如果是则尝试进行加锁,如果成功,则把自己变成head node,过程如下所示:

如果自己不是第一个排队的节点或者tryAcquire()失败,则调用``shouldParkAfterFailedAcquire(),其主要逻辑是使用CAS将节点状态由 INITIAL设置成SIGNAL,表示当前线程阻塞等待SIGNAL唤醒。如果设置失败,会在 acquireQueued()方法中的死循环中继续重试,直至设置成功。然后调用parkAndCheckInterrupt() 方法,把当前线程阻塞挂起,等待唤醒。parkAndCheckInterrupt()`的实现需要借助下层的能力,这是本文的重点,在下文中逐层阐述。