-
先判断线程是否被中止了
-
如果被中止,抛出异常,方法结束
-
如果没有被中止
-
调用tryAcquire方法去尝试获得锁(这个方法是AQS没有实现的,要锁去具体实现)
-
如果获得锁失败了,才会调用doAcquireNanos方法,这个方法就是超时等待实现的细节
[](
)doAcquireNanos
这里先贴上源码
private boolean doAcquireNanos(long arg, long nanosTimeout)
throws InterruptedException {
//判断设置的超时时间是否正确
//如果不正确,直接返回false
//那么上一层的tryAcquirenano方法整体就会返回false
//那么就不可以继续执行下去了
if (nanosTimeout <= 0L)
return false;
//计算获取锁的限制时间
//超过这个时间就不可以获取了
//System.nanoTime是一个本地方法,用来获取虚拟机时间的,精确到纳秒级别
//所以传进来的nanosTimeout必须为纳秒级别
// 1秒 = 10^9纳秒,十亿份之一
//这也是为什么使用long类型
final long deadline = System.nanoTime() + nanosTimeout;
//因为没抢到锁,所以要将该线程加入到队列里面
//并且nextWaiter为Exclusive,代表该结点线程状态是独占式的
final Node node = addWaiter(Node.EXCLUSIVE);
//failed变量记录过程是否出错
boolean failed = true;
try {
//死循环
for (;😉 {
//下面这段代码就是自旋获取锁的逻辑
//获取上一个线程
final Node p = node.predecessor();
//从下面这个判断可以判断出,这个抢锁过程是公平的
//如果上一个线程是头结点
//并且自己尝试获取锁成功
if (p == head && tryAcquire(arg)) {
//那么该线程就可以继续运行了
//先将头结点设为自己
setHead(node);
//断开与头结点的连接
//让头结点可以回收
p.next = null; // help GC
//failed为false代表自旋过程无出错
failed = false;
//自旋结束,返回true,回到上一层继续运行
return true;
}
//上一个不是头结点,或者虽然是头结点但抢不到锁
//计算还需要等待多长时间
nanosTimeout = deadline - System.nanoTime();
//如果没有时间剩余了
if (nanosTimeout <= 0L)
//返回false
//上一层处理
return false;
//还有时间剩余,看需不需要park
//这里一样是自旋两次,然后就会返回true,代表需要Park
//继续判断剩余时间是否足够进行park
//因为如果park需要的时间都要多于线程剩下的时间
//没什么必要park了,让其再自旋多一次,拿不到就超时返回false
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
//如果超时了,而且需要park,那就park掉
//这里park掉,线程也是会继续进行倒计时的
//在这段时间内都会被park掉,这段时间内,只有unpark和interrupt可以恢复
//超过这个时间也会恢复,然后又一次自旋,然后超时GG
LockSupport.parkNanos(this, nanosTimeout);
//判断唤醒该线程的方式是不是被别的线程interrupt
//因为park是不会覆盖interrupt标志位的
if (Thread.interrupted())
//如果是被中止
//抛出异常
throw new InterruptedException();
}
} finally {
//同理,没有修改过failed变量,抛出异常,超时
//就会执行下面cancelAcquire
if (failed)
cancelAcquire(node);
}
}
总结一下整个流程
-
判断等待时间是否合理
-
小于等于0,不合理,直接返回false
-
计算最大等待时间点,使用当前虚拟机的时间加上等待时间,纳秒级别
-
将当前线程添加到队列中
-
定义一个failed变量,该变量用来表示该线程最终的结果是否拿到锁,开始为false,因为刚开始没拿到
-
开始进行自旋
-
当前线程查看自己的上一个结点是不是头结点
-
如果是,再判断自己能不能抢到锁
-
如果抢到了锁,调整线程队列,将自己设为头结点,并且断开旧头结点与队列的连接,让其被gc回收,然后修改failed变量为true,代表拿到了锁
-
如果上一个结点不是头结点,或者,是头结点但抢不到锁
-
计算当前线程还剩下多少等待时间,使用最大等待时间点减去当前时间点
-
如果等待时间小于等于0,那就代表超时了,返回false
-
如果没有超时,判断这个线程需不需要park掉
-
如果自旋已经两次了,且剩余时间还足够,那就park掉,这个park会在指定一段时间后恢复回来,这一段 需要zi料+ 绿色徽【vip1024b】
时间就是指线程剩余的时间
-
如果在剩余时间没有被主动唤醒,自己也会醒,但下一轮的自旋就会被判断超时了
-
如果在剩余时间被主动唤醒,唤醒的动作有两种,一种是unpark,一种是interrupt
-
判断唤醒是不是interrupt方式,如果是,那就直接抛出异常
-
最后执行finally里面的判断
-
如果failed变量为true,也就是该线程一直获取不到锁,就会执行cancelAcquire方法
[](
)spinForTimeoutThreshold
下面我们来看一下,是如何判断剩余时间是否足够进行park的
从代码上来看,只要大于spinForTimeoutThreshold就i是剩余时间足够进行park
而这个spinForTimeoutThreshold具体的数值为1000,也就是1000纳秒
最后
由于篇幅原因,就不多做展示了
.(img-ZGxRL2yp-1710355839112)]
[外链图片转存中…(img-YcxR3z4b-1710355839113)]
[外链图片转存中…(img-G8hdXt8Z-1710355839113)]
[外链图片转存中…(img-eQpceqjo-1710355839114)]
[外链图片转存中…(img-EZW4VU0q-1710355839114)]
[外链图片转存中…(img-gQj3NzBd-1710355839115)]
由于篇幅原因,就不多做展示了