ReentrantLock 主要是通过 AQS + CAS 的方式实现加锁和解锁原理。
这里先说一下重要的几个属性。
- state 标记是否加锁成功或是否发生锁重入
- exclusiveOwnerThread 当前拥有锁的线程
- waitStatus 线程等待状态
变量 | 含义 |
---|---|
0 | 当一个 Node 被初始化的时候的默认值 |
CANCELLED | 1,表示线程取消获取锁 |
SIGNAL | -1,表示线程已经准备好,等待获取锁了 |
CONDITION | -2,表示线程在等待队列中,等待被唤醒 |
PROPAGATE | -3,线程处在 SHARED 情况下,该字段才会使用 |
ReentrantLock 有公平锁和非公平锁的实现,默认是非公平锁,可以通过构造方法来决定公平还是非公平。
// 默认构造器,非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
// 传入true 为公平锁,false 为非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
非公平锁实现原理
加锁流程
lock() 方法
final void lock() {
// CAS 尝试将 0 -> 1
if (compareAndSetState(0, 1))
// CAS 成功,当前线程加锁成功
setExclusiveOwnerThread(Thread.currentThread());
else
// CAS 失败
acquire(1);
}
线程首先尝试通过 CAS 将 0->1 ,CAS 成功则当前线程加锁成功,否则加锁失败,调用 acquire() 方法
acquire() 方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
这里首先分析一下 tryAcquire() 方法,最终调用的是 nonfairTryAcquire() 方法
nonfairTryAcquire() 方法
final boolean nonfairTryAcquire(int acquires) {
// 这里传入的 acquires == 1
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取 state 值
int c = getState();
// state == 0,表示还未加锁
if (c == 0) {
// 尝试 CAS 将 0 -> 1
if (compareAndSetState(0, acquires)) {
// CAS 成功,当前线程加锁成功
setExclusiveOwnerThread(current);
return true;
}
}
// 拥有锁的线程就是当前线程,说明发生了 锁重入
else if (current == getExclusiveOwnerThread()) {
// 锁重入计数加 1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 更新 state 值,锁重入计数加 1
setState(nextc);
return true;
}
return false;
}
在 nonfairTryAcquire() 方法中,线程会去尝试加锁,该方法有四个可能的结果。
- state == 0 时
- CAS 成功,加锁成功
- CAS 失败,加锁失败
- state != 0
- 当前线程之前已经获取到锁了,发生锁重入,计数加1,state 值加1
- 锁已经被其他线程获取到了,加锁失败
再来分析一下 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
addWaiter()
private Node addWaiter(Node mode) {
// 将当前线程包装为一个 Node 节点,并设置为独占模式
Node node = new Node(Thread.currentThread(), mode);
// 获取尾结点
Node pred = tail;
// 尾结点不为 null
if (pred != null) {
// 设置尾结点为当前节点的头结点
node.prev = pred;
// 尝试 CAS 将当前节点添加到队列尾部
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 该方法作用是将当前节点加入队列尾部
enq(node);
return node;
}
private Node enq(final Node node) {
// 无限循环尝试 CAS 将当前节点添加到队列尾部,直到添加成功才退出循环
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
addWaiter() 方法的主要作用是将当前线程包装成 Node 节点并添加到队列尾部。
acquireQueued() 方法
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
// 打断标记
boolean interrupted = false;
for (;;) {
// 获取当前线程的前置节点
final Node p = node.predecessor();
// 如果前置节点是头结点则再次调用 tryAcquire() 方法去加锁
// 只有前置节点是头结点,当前线程才有机会去获取锁
if (p == head && tryAcquire(arg)) {
// 当前线程加锁成功
// 设置当前节点为头结点
setHead(node);
// 垃圾回收前置节点
p.next = null; // help GC
failed = false;
return interrupted;
}
// 判断当前线程在获取锁失败之后是否应该等待
if (shouldParkAfterFailedAcquire(p, node) &&
// 线程进入等待并检查一下中断标记
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// 线程有中断标记
if (failed)
// 线程被打断,不再去获取锁
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire() 方法
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 获取前一个线程节点的状态
int ws = pred.waitStatus;
// 前一个线程还处于阻塞状态,未获取到锁
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
// 返回 true,表示当前阻塞当前线程
return true;
// 表示前一个节点已经取消获取锁了
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
// 找到所有取消获取锁的线程,并将他们从等待队列中删除
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
// 给前置节点设置标记为 Node.SIGNAL,这次先不将当前线程阻塞,以便于下次进入该方法的时候,将当前线程阻塞
// 这样做的好处是又可以去尝试获取一次锁
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
// 返回 false,表示不阻塞当前线程
return false;
}
parkAndCheckInterrupt() 方法
private final boolean parkAndCheckInterrupt() {
// 阻塞当前线程
LockSupport.park(this);
// 返回当前线程是否被打断
return Thread.interrupted();
}
acquireQueued() 方法中有两个主要作用,一是去阻塞当前线程,二是还会去尝试让当前线程去获取锁。
阻塞队列中能获取锁的线程只有队列中的第二个节点线程(当前节点的前置节点是头结点)。
解锁流程
unlock() 方法
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
// 尝试释放锁
if (tryRelease(arg)) {
// 获取头结点
Node h = head;
// 头结点不为 null 且状态不为 0
if (h != null && h.waitStatus != 0)
// 唤醒队列中的一个线程
unparkSuccessor(h);
return true;
}
return false;
}
tryRelease() 方法
protected final boolean tryRelease(int releases) {
// 将 state 值减 1
int c = getState() - releases;
// 当前线程并不是获取到锁的线程,抛异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
// 标记锁是否成功释放
boolean free = false;
// 值为0,表示锁已经成功释放
// 值不为0,表示之前发生了锁重入,state 计数减 1
if (c == 0) {
// 成功释放锁
free = true;
setExclusiveOwnerThread(null);
}
// 更新 state 值
setState(c);
return free;
}
tryRelease() 方法的主要作用有两个。
- state == 1 时,成功解锁
- state > 1 时,说明发生了锁重入,state 值减 1
unparkSuccessor() 方法
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
// 获取当前线程节点的状态(当前节点是头结点)
int ws = node.waitStatus;
// 当前线程节点状态不是 CANCELLED 状态
if (ws < 0)
// 尝试 CAS 将当前线程节点状态设置为 0
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
// 获取头结点的后置节点
Node s = node.next;
// 后置节点为 null 或者 后置节点状态为 CANCELLED
if (s == null || s.waitStatus > 0) {
s = null;
// 从后往前遍历等待队列,找到离头结点最近并且状态不为 CANCELLED 的一个节点
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
// 唤醒该线程
LockSupport.unpark(s.thread);
}
unparkSuccessor() 方法的主要作用是唤醒等待队列中离头结点最近的一个线程。
不可打断模式
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
// 打断标记
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
// 需要获取锁成功之后,才能返回这个打断标记
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
// 检查线程的打断标记
parkAndCheckInterrupt())、
// 设置打断标记为 true
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
// 返回并清除线程的打断标记
return Thread.interrupted();
}
可以看到,在不可打断模式下,仅仅只是设置了一下线程的打断标记,并没有实际性的打断操作。
可打断模式
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 加锁失败
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
// 检查打断标记,如果线程被 Thread.interrupt() 方法打断,返回true
parkAndCheckInterrupt())
// 线程被打断,抛出异常,结束 for(;;) 循环
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
在可打断模式下,线程如果被 Thread.interrupt() 方法设置了打断标记,则会抛出异常,线程被中断,停止去加锁
公平锁实现原理
加锁流程
tryAcquire() 方法
protected final boolean tryAcquire(int acquires) {
// 这里传入的 acquires == 1
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取 state 值
int c = getState();
// state == 0,表示还未加锁
if (c == 0) {
if (
// 判断是否有前驱结点
!hasQueuedPredecessors() &&
// 尝试 CAS 将 0 -> 1
compareAndSetState(0, acquires)) {
// CAS 成功,当前线程加锁成功
setExclusiveOwnerThread(current);
return true;
}
}
// 拥有锁的线程就是当前线程,说明发生了 锁重入
else if (current == getExclusiveOwnerThread()) {
// 锁重入计数加 1
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
// 更新 state 值,锁重入计数加 1
setState(nextc);
return true;
}
return false;
}
hasQueuedPredecessors() 方法
public final boolean hasQueuedPredecessors() {
// 尾结点
Node t = tail;
// 头结点
Node h = head;
Node s;
// h != t 队列中有在排队的线程结点
// (s = h.next) == null 只有头结点
// s.thread != Thread.currentThread() 头结点的下一个结点不是当前结点
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
公平锁相比较于非公平锁的实现方式就是多了一个 hasQueuedPredecessors() 方法的判断。这个方法是判断等待队列中是否还有其他线程排在当前结点前面。意思是,这个队列是一个先进先出的队列,排在前面的线程优先获取到锁。
解锁流程
和非公平锁的解锁流程相同
条件变量 Condition
调用 Condition.await() 方法实际上调用的是其实现类 ConditionObject.await()方法
我们先看下 ConditionObject
/** First node of condition queue. */
/** 等待队列中的第一个结点 */
private transient Node firstWaiter;
/** Last node of condition queue. */
/** 等待队列中的最后一个结点 */
private transient Node lastWaiter;
await() 方法
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 将当前线程结点添加到 ConditionWaiter 队列中
Node node = addConditionWaiter();
// "完全"释放线程持有的锁
// 因为线程可能发生重入,所有这里叫完全释放
long savedState = fullyRelease(node);
int interruptMode = 0;
// 当前线程结点没有添加到 AQS 阻塞队列中
while (!isOnSyncQueue(node)) {
// 阻塞该线程
LockSupport.park(this);
// 被打断,退出循环
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 尝试去获取锁,并且线程打断模式是不是 THROW_IE(抛出异常,可打断模式)
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
// 设置线程打断状态为 REINTERRUPT(不可打断模式)
interruptMode = REINTERRUPT;
// 清除队列中已取消等待的线程结点
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
// interruptMode != 0 说明了有打断标记
if (interruptMode != 0)
// 根据打断模式做出相应处理
reportInterruptAfterWait(interruptMode);
}
addConditionWaiter() 方法
private Node addConditionWaiter() {
// 获取尾结点
Node t = lastWaiter;
// 尾结点状态不是 CONDITION 状态
if (t != null && t.waitStatus != Node.CONDITION) {
// 清除队列中所有取消等待的线程结点
unlinkCancelledWaiters();
t = lastWaiter;
}
// 构造当前线程结点,状态为 CONDITION
Node node = new Node(Thread.currentThread(), Node.CONDITION);
// 将结点入队
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
fullyRelease() 方法
final long fullyRelease(Node node) {
boolean failed = true;
try {
long savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
isOnSyncQueue() 方法
// 判断线程结点是否在 AQS 队列中
final boolean isOnSyncQueue(Node node) {
// 没有在 AQS 队列中
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
// 已经在 AQS 队列中
if (node.next != null) // If has successor, it must be on queue
return true;
/*
* node.prev can be non-null, but not yet on queue because
* the CAS to place it on queue can fail. So we have to
* traverse from tail to make sure it actually made it. It
* will always be near the tail in calls to this method, and
* unless the CAS failed (which is unlikely), it will be
* there, so we hardly ever traverse much.
*/
// 从后向前遍历 AQS 队列,查看该线程结点是否在 AQS 队列中
return findNodeFromTail(node);
}
// 从后向前遍历 AQS 队列,查看该线程结点是否在 AQS 队列中
private boolean findNodeFromTail(Node node) {
Node t = tail;
for (;;) {
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
checkInterruptWhileWaiting() 方法
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
transferAfterCancelledWait() 方法
// 转换结点状态,等待状态 -> 初始状态
// 如果被设置取消唤醒的结点还没有被唤醒的话,返回 true
final boolean transferAfterCancelledWait(Node node) {
// CAS 将结点状态由 等待状态 -> 初始状态
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
enq(node);
return true;
}
/*
* If we lost out to a signal(), then we can't proceed
* until it finishes its enq(). Cancelling during an
* incomplete transfer is both rare and transient, so just
* spin.
*/
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
reportInterruptAfterWait() 方法
// 根据打断模式,做相应行为
private void reportInterruptAfterWait(int interruptMode)
throws InterruptedException {
// 打断模式为可打断模式
if (interruptMode == THROW_IE)
throw new InterruptedException();
// 打断模式为不可打断模式
else if (interruptMode == REINTERRUPT)
// 设置一个打断标记
selfInterrupt();
}
ConditionObject.await() 方法的功能是将线程结点加入 ConditionWaiter 队列,并调用 LockSupport.part() 方法暂停线程
signal() 方法
public final void signal() {
// 当前线程是否持有锁
// 如果当前线程不是持有锁的线程将会抛出异常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 获取 ConditionWaiter 队列中的第一个元素
Node first = firstWaiter;
if (first != null)
// 唤醒线程
doSignal(first);
}
isHeldExclusively() 方法
protected final boolean isHeldExclusively() {
// 当前线程是否持有锁
return getExclusiveOwnerThread() == Thread.currentThread();
}
doSignal() 方法
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
/*
* 改变线程状态,如果失败,说明线程结点已经取消被唤醒了
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 将线程结点加入到 AQS 队列中,并返回它的前置结点
Node p = enq(node);
int ws = p.waitStatus;
// ws > 0 前置结点已经取消被唤醒了
// 设置前置结点状态为 SIGNAL
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}