Java并发工具AbstractQueuedSynchronizer实现详解

CLH队列

在CLH队列中,每个线程的等待状态(waitState变量)保存在前一个结点中,取值可以是 1 (CANCELED,线程已经放弃等待),-1(SIGNAL,线程已经被阻塞、需要被唤醒),-2(CONDITION,线程在条件队列中等待,这种状态的结点不可能出现在CLH队列,只会出现在Condition条件队列上),-3(PROPAGATE,下一个在共享模式中等待的线程无条件唤醒,即唤醒动作可以继续往队列后面接力),0(就绪,这个状态代表线程没有被阻塞,并且准备好请求锁)。

3. 通过循环+CAS操作来对CLH队列进行修改

因为AQS本身就是用于实现锁的,所以AQS中的CLH队列没有锁来保护,且必须支持并发修改。怎么办?通过循环结合CAS操作来实现CLH队列操作的线程安全性。例如enq(),

private Node enq(final Node node) { for (;😉 { Node t = tailif (t == null) { // Must initialize if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } }}

4. 加锁操作如何实现

与CLH队列操作很类似,加锁操作也是通过循环+CAS操作来实现,不过还使用到了让线程阻塞的方法LockSupport.park()。我们以不可中断的加锁操作为例,讲解其主要实现逻辑如下:在一个死循环中,首先调用乐观锁加锁操作 tryAcquire(),如果成功,则加锁操作直接返回;如果失败了,则判断当前线程结点的前一结点的waitState:如果等于SIGNAL,则直接进入阻塞(说明此时已经有前一个结点得到锁了),如果是CANCELED,则清理一次CLH队列(把已经取消等待的线程中队列中移除)再次执行循环,如果是0或PROPAGATION,则把状态改为SIGNAL,继续执行循环。如果前面的阻塞操作被其他线程唤醒了,再次执行循环。参考代码:

final boolean acquireQueued(final Node node, int arg) {

boolean failed = true;

try {

boolean interrupted = false;

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);

}

}

加锁操作其实有计时版本、可中断版本,但大体逻辑就是上面这样,只不过在循环中再进行了一些时间判断、中断标志判断等,并体现在返回结果或抛出异常上。

总结一下,AQS的加锁操作就是在一个循环中,不断执行CAS加锁(成功则返回,失败则继续),然后不断阻塞和被唤醒(在每次被唤醒的时候,顺便执行一些CLH队列清理工作),再次执行CAS加锁。

5. 加锁操作优化

AQS中的加锁操作,即acquire(),进行了一种barge优化。意思就是,当锁被释放出来以后,此时正好有一个线程请求锁,而队列中的第一个线程也被唤醒并且请求锁,这两个线程谁将获得锁是不确定的。这种优化对AQS锁的并发性有提升,但是使得它变成不公平的锁(没有按照FIFO原则操作)。

public final void acquire(int arg) {

if (!tryAcquire(arg) &&

acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

selfInterrupt();

}

6. 解锁操作如何实现

解锁操作的实现比较简单,先调用一个在子类中实现的CAS解锁操作(tryRelease),如果成功,则把队列中的第一个结点移出队列,判断是否需要唤醒下一个线程,需要则唤醒。

总结

无论是哪家公司,都很重视高并发高可用的技术,重视基础,重视JVM。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。

最后我整理了一些面试真题资料,技术知识点剖析教程,还有和广大同仁一起交流学习共同进步,还有一些职业经验的分享。

面试了阿里,滴滴,网易,蚂蚁,最终有幸去了网易【面试题分享】

用于所有人,相信经过一些面试,大家都会有这些感触。

最后我整理了一些面试真题资料,技术知识点剖析教程,还有和广大同仁一起交流学习共同进步,还有一些职业经验的分享。

[外链图片转存中…(img-cDwNxI6Y-1721140647035)]

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值