简历竟然敢写精通并发编程,那你说说AQS为什么要用双向链表?

简介: 一位工作4年的程序员 , 简历上写了精通并发编程 , 并且还阅读过AQS(AbstractQueuedSynchronizer)的源码,然后面试官只问了这样一个问题:“AQS 为什么要采用双向链表结构”?,然后就垮了!其实AQS 大家都不陌生,它是 J.U.C 包里面一个非常重要的线程同步器。今天,我给大家聊聊我的理解。

一位工作4年的程序员 , 简历上写了精通并发编程 , 并且还阅读过AQS(

AbstractQueuedSynchronizer)的源码,然后面试官只问了这样一个问题:“AQS 为什么要采用双向链表结构”?,然后就垮了!

其实AQS 大家都不陌生,它是 J.U.C 包里面一个非常重要的线程同步器。今天,我给大家聊聊我的理解。

另外,我把往期分享的视频全部整理成一份500页的PDF面试题解析配套文档,希望能够以此来提高各位粉丝的通过率,

ee90d9963df444db88b33d6e798a5b94.gif

如何获取? :

扫描文章底部名片领取!

1、原因分析

首先,双向链表有两个指针,一个指针指向前置节点,一个指针指向后继节点。所以,双向链表可以支持常量 O(1) 时间复杂度的情况下找到前驱节点。因此,双向链表在插入和删除操作的时候,要比单向链表简单、高效。


从双向链表的特性来看,我认为 AQS 使用双向链表有三个方面的原因:

998e0ef9d5909f89e94a09f3e4965a00.jpg

第1个原因,没有竞争到锁的线程加入到阻塞队列,并且阻塞等待的前提是,当前线程所在节点的前置节点是正常状态,这样设计是为了避免链表中存在异常线程导致无法唤醒后续线程的问题。

57f92d228e5c9ba02052832e95aa7fb2.jpg

所以,线程阻塞之前需要判断前置节点的状态,如果没有指针指向前置节点,就需要从 Head 节点开始遍历,性能非常低。

dc0a6247e4d8470c9a661b66aca47a57.png

第2个原因,在 Lock 接口里面有一个,lockInterruptibly()方法,这个方法表示处于锁阻塞的线程允许被中断。

a987db133e1467948ced8da6e576c0a0.jpg

也就是说,没有竞争到锁的线程加入到同步队列等待以后,是允许外部线程通过interrupt()方法触发唤醒并中断的。这个时候,被中断的线程的状态会修改成 CANCELLED。而被标记为 CANCELLED 状态的线程,是不需要去竞争锁的,但是它仍然存在于双向链表里面。

这就意味着在后续的锁竞争中,需要把这个节点从链表里面移除,否则会导致锁阻塞的线程无法被正常唤醒。在这种情况下,如果是单向链表,就需要从 Head 节点开始往下逐个遍历,找到并移除异常状态的节点。同样效率也比较低,还会导致锁唤醒的操作和遍历操作之间的竞争。

ce2b5eca2e74bdd678ad7b72a0c2ece0.jpg

第3个原因,是为了避免线程阻塞和唤醒的开销,所以刚加入到链表的线程,首先会通过自旋的方式尝试去竞争锁。

60f526393f3ce8230d0489320a861026.jpg

但是实际上按照公平锁的设计,只有头节点的下一个节点才有必要去竞争锁,后

续的节点竞争锁的意义不大。否则,就会造成羊群效应,也就是大量的线程在阻塞之前尝试去竞争锁带来比较大的性能开销。

所以,为了避免这个问题,加入到链表中的节点在尝试竞争锁之前,需要判断前置节点是不是头节点,如果不是头节点,就没必要再去触发锁竞争的动作。所以这里会涉及到前置节点的查找,如果是单向链表,那么这个功能的实现会非常复杂。

这个问题,有可能99%的人都回答不上来。对 AQS 理解不深刻的情况下,可能不知道如何回答。理解一个技术为什么这么设计,关键在于它需要解决什么样的问题。

最后,我把往期分享的面试题全部整理成了1份10W字的文档,希望能够以此来提高各位粉丝的通过率

ee90d9963df444db88b33d6e798a5b94.gif

我是被编程耽误的文艺Tom,只弹干货不掺水!你们的支持就是我最大的动力!关注我,面试不再难!

相关文章
|
8天前
|
存储 缓存 Java
面试官:什么是Java内存模型?
面试官:什么是Java内存模型?
118 0
面试官:什么是Java内存模型?
|
8天前
|
存储 算法 Java
到底什么是AQS?面试时你能说明白吗!
【5月更文挑战第1天】到底什么是AQS?面试时你能说明白吗!
26 4
|
8天前
|
设计模式 存储 Java
【面试问题】什么是 AQS ?
【1月更文挑战第27天】【面试问题】什么是 AQS ?
|
8天前
|
设计模式 数据采集 存储
Java并发编程学习7-阻塞队列
本篇介绍阻塞队列相关的内容(Queue、BlockingQueue、Deque 和 BlockingDeque)
53 2
Java并发编程学习7-阻塞队列
|
8天前
|
安全 Java 程序员
线程安全问题(面试常考)
线程安全问题(面试常考)
61 0
线程安全问题(面试常考)
|
7月前
|
设计模式 Java API
终于弄懂AQS了
大家好,我是三友~~ 相信大家对Java中的Lock锁应该不会陌生,比如ReentrantLock,锁主要是用来解决解决多线程运行访问共享资源时的线程安全问题。那你是不是很好奇,这些Lock锁api是如何实现的呢?本文就是来探讨一下这些Lock锁底层的AQS(AbstractQueuedSynchronizer)到底是如何实现的。
|
8月前
|
设计模式 Java 容器
一篇神文就把java多线程,锁,JMM,JUC和高并发设计模式讲明白了
今天给大家分享一篇一线开发大牛整理的java高并发核心编程神仙文档,里面主要包含的知识点有:多线程、线程池、内置锁、JMM、CAS、JUC、高并发设计模式、Java异步回调、CompletableFuture类等。
|
9月前
|
Java 开发者
每天一道面试题之-AQS
每天一道面试题之-AQS
86 0
|
11月前
|
Java C++
说一下 synchronized 底层实现原理?(高薪常问)
说一下 synchronized 底层实现原理?(高薪常问)
82 2