一、前言
ReentrantLock源码看了很多遍,也总是忘,这次把源码逻辑仔仔细细的写一遍,为了供以后复习,也给各位正在学习的同学们一个参考。
二、ReentrantLock简介
相信大家都熟悉,ReentrantLock是一种基于AQS框架的应用实现,是JDK中的一种线程并发访问的同步手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全。
三、加锁逻辑
假设有3个线程1,2,3依次去获取锁。
ReentrantLock#lock
final void lock() {
// 使用CAS修改State的值,从0改成1,修改成功则加锁成功。
if (compareAndSetState(0, 1))
// 加锁成功把当前线程设置为独占线程
setExclusiveOwnerThread(Thread.currentThread());
else
// 再次获取锁,还是获取失败添加到等待队列
acquire(1);
}
AQS#acquire
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
AQS#tryAcquire:此方法会先获取state的值,如果是0,再CAS尝试获取一下锁,获取成功就返回了,代表加锁成功。同时也会判断一下是否是冲入锁,如果当前线程和getExclusiveOwnerThread是同一个,说明是重入锁,State+1,同时代表加锁成功。否则代表加锁失败。
AQS#addWaiter:如果加锁失败,会进入该方法,主要是入双向队等待操作。
AQS#acquireQueued:
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())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
- 进入死循环,进入node的前一个节点,如果是头节点,会尝试再获取一次锁,获取成功返回。这里算是设计的巧妙之处,因为阻塞当前线程也是在这个方法里,当线程被唤醒,这时候可以调用尝试获取锁,直接获取到锁。
- 如果没有获取成功,会调用shouldParkAfterFailedAcquire,该方法主要是监测更新status的值。刚开始ws是0,并不是-1,所以使用CSA把节点的Status改成-1,返回flase。同时再次进入死循环。这时候shouldParkAfterFailedAcquire返回的是true,执行parkAndCheckInterrupt。
AQS 定义了5个队列中节点状态:
1.值为0,初始化状态,表示当前节点在sync队列中,等待着获取锁。
2.CANCELLED,值为1,表示当前的线程被取消;
3.SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
4.CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
5.PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
- parkAndCheckInterrupt主要是调用LockSupport.park(this);,使当前线程阻塞。
四、解锁逻辑
AQS#release
public final boolean release(int arg) {
// 尝试进行解锁
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
// 执行locksupport.unpark
unparkSuccessor(h);
return true;
}
return false;
}
AQS#tryRelease
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;
}
- 获取state,然后减一,如果等于0,说明可以释放锁,返回true
- 获取等待队列头结点,如果头节点不为空,并且不是0的时候,进行unparkSuccessor唤醒头结点的下一个节点操作。同时会把节点状态改为0。唤醒完成之后,会继续执行AQS#acquireQueued的步骤一,使当前线程抢占锁,同时进行出队。