前言
AQS、ReentrantLock解锁源码分析
如果没看过上篇加锁的文章,也不影响观看这一篇文章,可以放心食用,
如果想彻底搞明白,建议先看上一篇,在看这一篇文章,这一篇主要是对上一篇没讲完的进行补充。
传送门:一篇文章彻底搞懂AQS、ReentrantLock“加锁”分析上(源码级别,深度剖析)
一、lock源码分析
ReentrantLock lock = new ReentrantLock(true);//false为非公平锁,true为公平锁
lock.lock(); //加锁
lock.unlock(); //解锁
主要以第三行代码进行分析,看他做了什么事情!
1、release-释放锁
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
// 1、调用tryRelease尝试释放,主要看能不能把持有锁线程置空
if (tryRelease(arg)) {
// 2、需要注意,这里有点绕,为什么这里就算不进if,他也会执行返回true,是因为如果只有一个线程加锁,
// 同步队列可能还没有初始化,所以默认也是释放锁成功
Node h = head;
// 2、头部节点非空,并且不是刚初始化的,就调用底层释放锁操作
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
主要做几件事情
1、调用tryRelease尝试释放,主要看能不能把持有锁线程置空
2、需要注意,这里有点绕,为什么这里就算不进if,他也会执行返回true,是因为如果只有一个线程加锁,同步队列可能还没有初始化,所以默认也是释放锁成功
3、头部节点非空,并且不是刚初始化的,就调用底层释放锁操作
2、tryRelease-尝试释放锁线程
protected final boolean tryRelease(int releases) {
// 1、同步状态减一
int c = getState() - releases;
// 2、判断持有锁线程是不是当前线程,不是则抛异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 3、状态为0,代表可以去释放锁,把持有锁线程置空。
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
// 4、更新同步状态
setState(c);
return free;
}
主要做了几件事情:
1、同步状态减一
2、判断持有锁线程是不是当前线程,不是则抛异常
3、状态为0,代表可以去释放锁,把持有锁线程置空
4、更新同步状态
3、unparkSuccessor-释放锁的核心方法
private void unparkSuccessor(Node node) {
// 1、获取头部节点状态
int ws = node.waitStatus;
// 2、 在lock锁的情况下,这里一的队列头部节点为-1,把他置为0,最关键的原因是,在非公平锁的情况下,
// 新线程非公平加锁就直接会尝试获取锁,可能直接抢到锁,导致头部的下一节点进行抢锁失败,又多2次循环遍历,引起不必要的开销
if (ws < 0)
// 3、cas更新状态
compareAndSetWaitStatus(node, ws, 0);
// 4、如果头部节点的下一节点,是废弃节点(被中断、或者异常的线程),会把最靠进头部的节点拿到,进行唤醒
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;
}
// 5、执行唤醒操作
if (s != null)
LockSupport.unpark(s.thread);
}
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
主要做了几件事情
1、获取头部节点状态
2、 在lock锁的情况下,这里一的队列头部节点为-1,把他置为0,最关键的原因是,在非公平锁的情况下,新线程非公平加锁就直接会尝试获取锁,可能直接抢到锁,导致头部的下一节点进行抢锁失败,又多2次循环遍历,引起不必要的开销
3、cas更新状态
4、如果头部节点的下一节点,是废弃节点(被中断、或者异常的线程),会把最靠进头部的节点拿到,进行唤醒
5、执行唤醒操作
二、解锁流程图

总结
学习AQS源码,需要反复多看几次,因为他是一套代码兼容多种情况,如一个线程加锁、多个线程加锁情况等等,加锁命名不规范。
会容易学迷糊。
简单的说,解锁操作主要做了几件事情
1、更新状态码,直到状态码为0位置
2、释放锁,并且尝试唤醒头部节点的下一节点(同步队列里有阻塞线程的情况下)
本文深入剖析ReentrantLock解锁过程,包括release方法、tryRelease方法及unparkSuccessor核心方法的工作原理。揭示如何更新状态码并释放锁,以及如何尝试唤醒等待线程。
1343

被折叠的 条评论
为什么被折叠?



