并发编程之ReentrantLock解锁流程
目录
一、解锁流程
情景:线程t1持有锁,t2在park。然后t1释放锁,唤醒t2
解锁之前AQS(NonfairSync)的状态。重点关注waitStatus的值和当前线程拥有者。

图1
解锁之后AQS(NonfairSync)的状态。

图2
二、代码执行流程
//1. ReentrantLock.java
public void unlock() {
sync.release(1);
}
//2. ReentrantLock.java
/**
2.1 tryRelease(arg) 尝试释放锁
2.2 unparkSuccessor(h)
2.3 LockSupport.unpark(s.thread); 会调用acquireQueued方法
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {
//拿到当前头节点
Node h = head;
//h.waitStatus == -1 因为他需要唤醒t2线程。在t2线程入队的时候已经将它的前一个节点也就是head的waitStatus值设置为了-1
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
//当前状态值减1 c=0 (c不等于0的时候是重入,我们当前场景c一定等于0)
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//c=0 条件成立
if (c == 0) {
free = true;
//设置当前线程为空
setExclusiveOwnerThread(null);
}
//设置当前状态为c=0
setState(c);
return free;
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
//ws == -1 将当前head的waitStatus值改为0 表示当前没有责任再去叫醒下一个节点。这里这么做是防止在并发情况下,别的线程也会去叫醒下一个节点
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
//拿到head的下一个节点 也就是t2线程
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
//会进入到 LockSupport.unpark(s.thread)方法
if (s != null)
LockSupport.unpark(s.thread);
}
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
/**
此时 自选加锁成功 在setHead()方法中将head节点从队列中出队。t2线程加锁成功
*/
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);
}
}
版权声明:本文为yibailiheng原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。