ReentrantLock源码解析
java.util.concurrent包中的工具实现核心都是AQS,了解ReentrantLock的实现原理,需要先理解AQS
还没有看AQS的建议先去了解AQS
ReentrantLock构造
在创建ReentrantLock时会给我们提供2种选择公平的个非公平的实现,在上面都不传的情况下默认就是非公平NonfairSync
NonfairSync (不公平锁)
lock
第一个判断compareAndSetState(0, 1)
通过cas的方式获取尝试把0修改为1,如果修改成功那么就设置锁的持有者为当前线程,如果获取锁失败执行acquire
方法
在acquire
方法里会再一次尝试获取到锁,如果还是失败,调用addWaiter
构建Node队列
- 黄色三角标识该node的waitStatus状态,其中0代表默认的正常状态
- 其中第一个node用来占位,不关联线程称为哨兵
接下来是进入acquireQueued
方法
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 获取当前节点的前驱节点
final Node p = node.predecessor();
/**
如果获取到的节点是head头节点,因为head是一个空节点
说明他排在第一个,tryAcquire又去尝试获取锁,如果获取到
锁
**/
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
/**
如果还是获取锁失败则进入阻塞队列
调用parkAndCheckInterrupt方法进行等待
**/
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
如果获取到的节点是head头节点,因为head是一个空节点
说明他排在第一个,tryAcquire又去尝试获取锁,如果还是失败则会调用shouldParkAfterFailedAcquire
把前驱节点改为-1,节点状态为-1的意思是它有职责去唤醒它的下一个节点
parkAndCheckInterrupt方法阻塞线程进行等待
如果多个线程经历竞争则会变成这样
lockInterruptibly(可中断的锁)
进入acquireInterruptibly方法第一步判断线程是否被打断过,如果打断则抛出异常,如果沒有被打断则开始竞争锁,如果获取失败进入doAcquireInterruptibly方法
public final void acquireInterruptibly(int arg)
throws InterruptedException {
// 判断线程是否被打断
if (Thread.interrupted())
// 如果被打断抛出异常
throw new InterruptedException();
if (!tryAcquire(arg)) // 尝试获取到锁
// 获取失败
doAcquireInterruptibly(arg);
}
发现和lock的acquireQueued区别不大
一个是lock的acquireQueued只是返回了一个是否打断过的标识,而doAcquireInterruptibly方法则是直接抛出异常来停止线程
可重入
查询尝试获取锁的方法tryAcquire
nonfairTryAcquire里面就实现了可重入的机制
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获取AQS State状态
int c = getState();
// 如果是0说明是没有持有者的状态
if (c == 0) {
// 通过CAS来改变State状态 从0改为1来尝试获取锁
if (compareAndSetState(0, acquires)) {
//设置当前锁的持有者为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
// 如果状态不是0 说明该锁有持有者
// 判断持有者是不是和当前来获取锁的线程是否是同一个线程
else if (current == getExclusiveOwnerThread()) {
// 如果是同一个线程 进行状态的+1 重入记数
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 重新设置AQS同步器State的值
setState(nextc);
return true;
}
return false;
}
unLock
调用release方法
进入tryRelease判断
protected final boolean tryRelease(int releases) {
// 获取到状态-1
int c = getState() - releases;
// 解锁只能是锁的持有线程 其他线程报错
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 因为这里涉及到重入机制 可能减去1 过后依然不是0
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
// 重新设置AQS State值
setState(c);
return free;
}
public final boolean release(int arg) {
if (tryRelease(arg)) {// 走出tryRelease判断
Node h = head;
// 检查头结点后面是否还有待唤醒的线程 (waitStatus = -1 标识后面有线程等待唤醒)
if (h != null && h.waitStatus != 0)
// 进入unparkSuccessor 去唤醒后面等待的线程
unparkSuccessor(h);
return true;
}
return false;
}
走出 tryRelease方法后检查头结点后面是否还有待唤醒的线程 (waitStatus = -1 标识后面有线程等待唤醒),进入unparkSuccessor 去唤醒后面等待的线程
NonfairSync (公平锁)
公平锁和非公平锁的主要区别在于tryAcquire方法
--------------------非公平锁---------------------
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;
}
--------------------公平锁---------------------
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 先用hasQueuedPredecessors方法检查是否队列里面还有排队的线程
// 如果有等待的线程条件不成立不会去竞争锁
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;
}
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
// h!= t 头不等于尾的时候说明有等待的线程
return h != t &&
// 没有第二个node 因为第一个node是占位符
((s = h.next) == null
//或者第二个node不是当前线程
|| s.thread != Thread.currentThread());
}
await
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
先判断当前线程是否被打断过addConditionWaiter方法向等待的node链表申请一个node当前节点的状态为-2 表示等待的node线程,fullyRelease方法去释放当前线程获取所以的锁(包括多次重入的也要释放–并去唤醒后面等待执行的线程)
调用 LockSupport.park(this)阻塞自己
signal
判断当前线程是否是锁的持有者,然后去获取阻塞队列对一个node节点执行doSignal方法
private void doSignal(Node first) {
do {
// 如果没有下一个
if ( (firstWaiter = first.nextWaiter) == null)
// 就把最后一个设置为null
lastWaiter = null;
// 断开连接
first.nextWaiter = null;
// 转移到等待获取到锁的队列
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
// enq方法插入该节点到尾部,返回前驱节点
Node p = enq(node);
int ws = p.waitStatus;
// CAS修改前驱节点的状态为-1 说明他有责任唤醒后面节点
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
// 然后唤醒
LockSupport.unpark(node.thread);
return true;
}
enq方法插入该节点到尾部,返回前驱节点,通过CAS修改前驱节点的状态为-1 说明他有责任唤醒后面节点, LockSupport.unpark唤醒待获取锁的队列里面的线程