private Node addWaiter(Node mode) {
//新建一个节点
Node node = new Node(Thread.currentThread(), mode);
//这里先尝试把新节点加到尾节点后面
//如果成功了就返回新节点
//如果没成功再调用enq()方法不断尝试
Node pred = tail;
//如果尾节点不为空
if (pred != null) {
//设置新节点的前置节点为现在的尾节点
node.prev = pred;
// CAS更新尾节点为新节点
if (compareAndSetTail(pred, node)) {
//如果成功,把旧尾节点的下一个节点指向新节点
pred.next = node;
//返回新节点
return node;
}
}
//如果上面尝试入队新节点没成功,调用enq()处理
enq(node);
return node;
}
//AbstractQueuedSynchronizer.enq()
private Node enq(final Node node) {
//自旋,不断尝试
for (;😉 {
Node t = tail;
//如果尾节点为空,说明还未初始化
if (t == null) { // Must initialize
//初始化头节点和尾节点
if (compareAndSetHead(new Node()))
tail = head;
} else {
//如果尾节点不为空
//设置新节点的前一个节点为现在的尾节点
node.prev = t;
//CAS更新尾节点为新节点
if (compareAndSetTail(t, node)) {
//成功了,则设置旧尾节点的下一个节点为新节点
t.next = node;
//返回旧节点
return t;
}
}
}
}
//AbstractQueuedSynchronizer.acquireQueued()
//调用上面的addWaiter()方法使得新节点已经成功入队了,这个方法是尝试让当前节点来获取锁的
final boolean acquireQueued(final Node node, int arg) {
//失败标记
boolean failed = true;
try {
//中断标记
boolean interrupted = false;
//自旋
for (;😉 {
//当前节点的前一个节点
final Node p = node.predecessor();
//如果当前节点的前一个节点为head节点,则说明轮到自己获取锁了
//调用ReentrantLock.FairSync.tryAcquire()方法再次尝试获取锁
if (p == head && tryAcquire(arg)) {
//尝试获取锁成功
//这里同时只会有一个线程在执行,所以不需要用CAS更新
//把当前节点设置为新的头节点
setHead(node);
//并把上一个节点从链表中删除
p.next = null; // help GC
//标记为未失败
failed = false;
//还是需要获得锁后, 才能返回打断状态
return interrupted;
}
//是否需要阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
//真正阻塞的方法
parkAndCheckInterrupt())
//如果中断了
interrupted = true;
}
} finally {
//如果失败了
if (failed)
//取消获取锁
cancelAcquire(node);
}
}
//AbstractQueuedSynchronizer.shouldParkAfterFailedAcquire()
//第一次调用会把前一个节点的等待状态设置为SIGNAL,并返回false
//第二次调用才会返回true
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//上一个节点的等待状态
//注意Node的waitStatus字段我们在上面创建Node的时候并没有指定
// 也就是说使用的是默认值0
//这里把各种等待状态再贴出来
// static final int CANCELLED = 1;
// static final int SIGNAL= -1;
// static final int CONDITION = -2;
// static final int PROPAGATE = -3;
int ws = pred.waitStatus;
// 如果等待状态为SIGNAL(等待唤醒),直接返回true
if (ws == Node.SIGNAL)
return true;
// 如果前一个节点的状态大于0,也就是已取消状态
if (ws > 0) {
// 把前面所有取消状态的节点都从链表中删除
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 如果前一个节点的状态小于等于0,则把其状态设置为等待唤醒
// 这里可以简单地理解为把初始状态0设置为SIGNAL
// CONDITION是条件锁的时候使用的
// PROPAGATE是共享锁使用的
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
// AbstractQueuedSynchronizer.parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
//阻塞当前线程
//底层调用的是Unsafe的park()方法
LockSupport.park(this);
//返回是否已中断
return Thread.interrupted();
}
// AbstractQueuedSynchronizer.parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
//阻塞当前线程
//底层调用的是Unsafe的park()方法
LockSupport.park(this);
//返回是否已中断
return Thread.interrupted();
}
lock()
方法的主要流程:
-
ReentrantLock#lock()
-
ReentrantLock.
FairSync#lock()
// 公平模式获取锁 -
AbstractQueuedSynchronizer#
acquire()
// AQS的获取锁方法 -
ReentrantLock.FairSync#
tryAcquire()
// 尝试获取锁 -
AbstractQueuedSynchronizer#
addWaiter()
// 添加到队列 -
AbstractQueuedSynchronizer#
enq()
// 入队 -
>AbstractQueuedSynchronizer#
acquireQueued()
// 里面有个for()循环,唤醒后再次尝试获取锁 -
AbstractQueuedSynchronizer#
shouldParkAfterFailedAcquire()
// 检查是否要阻塞 -
AbstractQueuedSynchronizer#
parkAndCheckInterrupt()
// 真正阻塞的地方
公平锁获取锁的主要过程大致如下:
-
尝试获取锁,如果获取到了就直接返回了
-
尝试获取锁失败,再调用
addWaiter()
构建新节点并把新节点入队 -
然后调用
acquireQueued()
再次尝试获取锁,如果成功了,直接返回 -
如果再次失败,再调用
shouldParkAfterFailedAcquire()
将节点的等待状态置为等待唤醒(SIGNAL) -
调用
parkAndCheckInterrupt()
阻塞当前线程 -
如果被唤醒了,会继续在
acquireQueued()
的for()
循环再次尝试获取锁,如果成功了就返回 -
如果不成功,再次阻塞,重复(3)(4)(5)直到成功获取到锁
独占式同步状态获取流程,也就是acquire(int arg)
方法调用流程:
5.2 非公平锁实现
//ReentrantLock.lock();
public void lock() {
sync.lock();
}
// ReentrantLock.NonfairSync.lock()
final void lock() {
//直接尝试CAS更新状态变量
if (compareAndSetState(0, 1))
//如果更新成功,说明获取到了锁,把当前线程设为独占线程
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
//AbstractQueuedSynchronizer.acquire()
public final void acquire(int arg) {
//先尝试加锁
//如果失败了,就排队
if (!tryAcquire(arg) &&
//注意addWaiter()这里传入的节点模式为“独占模式”
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//ReentrantLock.NonfairSync.tryAcquire
protected final boolean tryAcquire(int acquires) {
//调用父类的方法
return nonfairTryAcquire(acquires);
}
// ReentrantLock.Sync.nonfairTryAcquire()
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取状态值
int c = getState();
if (c == 0) {
//如果状态变量为0,再次尝试CAS更新状态变量的值
//相对于公平锁模式少了!hasQueuedPredecessors()条件,不会检查AQS队列
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 如果已经获得了锁, 线程还是当前线程, 表示发生了锁重入
else if (current == getExclusiveOwnerThread()) {
//state++
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error(“Maximum lock count exceeded”);
setState(nextc);
return true;
}
return false;
}
相对于公平锁,非公平锁加锁的过程主要有两点不同:
-
一开始就尝试CAS更新状态变量state的值,如果成功了就获取到锁了
-
在tryAcquire()的时候没有检查是否前面有排队的线程,直接上去获取锁才不管别人有没有排队呢
总的来说,相对于公平锁,非公平锁在一开始就多了两次直接尝试获取锁的过程。
可中断地获取锁,和 lock()方法的不同之处在于该方法会响应中断,即在锁的获取中可以中断当前线程
//ReentrantLock.lockInterruptibly()
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
//AbstractQueuedSynchronizer.acquireInterruptibly(int arg)
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//如果没有获得到锁,进入doAcquireInterruptibly方法
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) &&
parkAndCheckInterrupt())
//在park过程中如果被interrupt会进入这里
//这时候抛出一次,而不会再次进入for循环
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
尝试非阻塞的获取锁,调用该方法后立刻返回,如果能够获取则返回true,否则返回false
// ReentrantLock.tryLock()
public boolean tryLock() {
//直接调用Sync的nonfairTryAcquire()方法
return sync.nonfairTryAcquire(1);
}
// ReentrantLock.Sync.nonfairTryAcquire()
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取状态值
int c = getState();
if (c == 0) {
//如果状态变量为0,再次尝试CAS更新状态变量的值
//相对于公平锁模式少了!hasQueuedPredecessors()条件
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;
}
tryLock()
方法比较简单,直接以非公平的模式去尝试获取一次锁,获取到了或者锁本来就是当前线程占有着就返回true,否则返回false。
8.tryLock(long time, TimeUnit unit)方法
超时的获取锁,当前线程在以下3种情况下会返回:
①当前线程在超时时间内获得了锁
②当前线程在超时时间内被中断
③超时时间结束,返回false
//ReentrantLock.tryLock()
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
// 调用AQS中的方法
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
// AbstractQueuedSynchronizer.tryAcquireNanos()
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
//如果线程中断,抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//先尝试获取一次锁
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
//AbstractQueuedSynchronizer.doAcquireNanos()
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
//如果时间到期,直接返回false
if (nanosTimeout <= 0L)
return false;
//到期时间
final long deadline = System.nanoTime() + nanosTimeout;
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 true;
}
nanosTimeout = deadline - System.nanoTime();
//如果时间到期,直接返回false
if (nanosTimeout <= 0L)
return false;
//spinForTimeoutThreshold = 1000L;
//只有到期时间大于1000纳秒才阻塞
//小于等于1000纳秒,直接自旋解决
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
// 阻塞一段时间
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
tryLock(long time, TimeUnit unit)
方法在阻塞的时候加上阻塞时间,并且会随时检查是否到期,只要到期了没获取到锁就返回false。
释放锁
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
//调用AQS的()方法释放锁
if (tryRelease(arg)) {
Node h = head;
//如果头节点不为空,且等待状态不是0,就唤醒下一个节点
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
//ReentrantLock.Sync.tryRelease
protected final boolean tryRelease(int releases) {
//state–
int c = getState() - releases;
// 如果当前线程不是占有着锁的线程,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//如果状态变量的值为0了,说明完全释放了锁
//这也就是为什么重入锁调用了多少次lock()就要调用多少次unlock()的原因
//如果不这样做,会导致锁不会完全释放,别的线程永远无法获取到锁
if (c == 0) {
free = true;
// 清空占有线程
setExclusiveOwnerThread(null);
}
//设置状态变量的值
setState©;
return free;
}
private void unparkSuccessor(Node node) {
//这里node表示头节点
int ws = node.waitStatus;
//如果头节点的等待状态小于0,就把它设置为0
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
//头节点的下一个节点
Node s = node.next;
//如果下一个节点为空,或者其等待状态大于0(实际为已取消)
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;
}
//如果下一个节点不为空,则唤醒它
if (s != null)
LockSupport.unpark(s.thread);
}
释放锁的过程大致为:
-
将state的值减1
-
如果state减到了0,说明已经完全释放锁了,唤醒下一个等待着的节点
public class ConditionObject implements Condition, java.io.Serializable {
/**
- First node of condition queue.
*/
private transient Node firstWaiter;
/**
- Last node of condition queue.
*/
private transient Node lastWaiter;
}
可以看到条件锁中也维护了一个队列,为了和AQS的队列区分,我这里称为条件队列,firstWaiter是队列的头节点,lastWaiter是队列的尾节点。
// ReentrantLock.newCondition()
public Condition newCondition() {
return sync.newCondition();
}
// ReentrantLock.Sync.newCondition()
final ConditionObject newCondition() {
return new ConditionObject();
}
// AbstractQueuedSynchronizer.ConditionObject.ConditionObject()
public ConditionObject() {
}
新建一个条件锁最后就是调用的AQS
中的ConditionObject
类来实例化条件锁。
// AbstractQueuedSynchronizer.ConditionObject.await()
public final void await() throws InterruptedException {
// 如果线程中断了,抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//添加节点到Condition的队列中,并返回该节点
Node node = addConditionWaiter();
//完全释放当前线程获取的锁,因为锁是可重入的,所以这里要把获取的锁全部释放
int savedState = fullyRelease(node);
int interruptMode = 0;
//如果该节点还没有转移至 AQS 队列, 阻塞
while (!isOnSyncQueue(node)) {
//阻塞当前线程
LockSupport.park(this);
//如果被打断, 退出等待队列
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//退出等待队列后, 还需要获得 AQS 队列的锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
//清除取消的节点
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
//线程中断相关
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
// AbstractQueuedSynchronizer.ConditionObject.addConditionWaiter
private Node addConditionWaiter() {
Node t = lastWaiter;
// 如果条件队列的尾节点已取消,从头节点开始清除所有已取消的节点
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
//重新获取尾节点
t = lastWaiter;
}
//新建一个节点,它的等待状态是CONDITION
Node node = new Node(Thread.currentThread(), Node.CONDITION);
//如果尾节点为空,则把新节点赋值给头节点(相当于初始化队列)
if (t == null)
firstWaiter = node;
//否则把新节点赋值给尾节点的nextWaiter指针
else
t.nextWaiter = node;
//尾节点指向新节点
lastWaiter = node;
//返回新节点
return node;
}
//AbstractQueuedSynchronizer.fullyRelease
final int fullyRelease(Node node) {
boolean failed = true;
try {
//获取状态变量的值,重复获取锁,这个值会一直累加
//这个值也代表着获取锁的次数
int savedState = getState();
//一次性释放所有获得的锁
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
//新建一个节点,它的等待状态是CONDITION
Node node = new Node(Thread.currentThread(), Node.CONDITION);
//如果尾节点为空,则把新节点赋值给头节点(相当于初始化队列)
if (t == null)
firstWaiter = node;
//否则把新节点赋值给尾节点的nextWaiter指针
else
t.nextWaiter = node;
//尾节点指向新节点
lastWaiter = node;
//返回新节点
return node;
}
//AbstractQueuedSynchronizer.fullyRelease
final int fullyRelease(Node node) {
boolean failed = true;
try {
//获取状态变量的值,重复获取锁,这个值会一直累加
//这个值也代表着获取锁的次数
int savedState = getState();
//一次性释放所有获得的锁
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-PYiuwJqM-1715144625298)]
[外链图片转存中…(img-0R28C86W-1715144625300)]
[外链图片转存中…(img-UOqNJhtM-1715144625301)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!