Java并发编程深度解析面试题:从AQS到ConcurrentHashMap,深入理解JUC

1. AQS是什么?

AQS即AbstractQueuedSynchronizer,是一个抽象类。它是Java并发工具包(java.util.concurrent)下的基础类,向上抽取了一些公共功能,但内部并没有具体功能实现。ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch等都是基于AQS实现的。

AQS的核心内容有两个:

  1. 由volatile修饰的基于CAS修改的state
  2. 一个双向链表,每一个节点都是Node对象,Node节点中可以存储当前是互斥锁还是共享锁,同时Node中还会存储状态信息和线程。

1.1 CAS

CAS(Compare and Swap,比较并交换)的本质是保证替换一个内存中的值时是原子性的。Java中很多内容都是基于CAS实现的,如AtomicInteger、LongAdder、ReentrantLock等。

image.png

通过cmpxchg指令实现,单核采用cmpxchg,多核需要追加前缀lock。

CAS的问题:

  • ABA问题:存在多线程操作,导致一个数据经历多次修改后,回到原值,导致不应该成功的CAS操作成功。可以通过AtomicStampedReference追加版本号解决。
  • 自旋次数过多:CAS不会挂起线程,会让CPU一直调度当前线程执行CAS直到成功,可能会过分消耗CPU资源。可以使用自适应自旋锁,或者分段锁(如LongAdder)避免此问题。
  • 只能对一个数值修改做原子性操作,无法锁住一段代码。

image.png

1.3 ThreadLocal的两个内存泄漏问题

ThreadLocal只是一个工具,真正存储数据的是Thread类中的ThreadLocalMap。

ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

image.png

ThreadLocal的内存泄漏有两点:

  1. key的内存泄漏:为了解决key的内存泄漏问题,ThreadLocal作为key存在时,是一个弱引用,只要执行GC时,没有强引用指向,必然会被回收。
  2. value的内存泄漏:尤其在使用线程池时,线程可能不会立即销毁,造成value无法及时回收。需要在使用完ThreadLocal后,及时调用remove方法。

image.png

2. AQS为什么有一个虚拟的head节点

虚拟的head节点可以知道后继节点是否已经挂起。如果挂起需要找到离head最近的有效节点唤醒,这个找有效节点的操作可能需要从tail遍历到head。

image.png

3. ReentrantLock释放锁时为什么要从后往前找有效节点?

因为在addWaiter方法中,需要先将当前节点的prev指向前继节点,然后基于CAS将tail指向当前节点,最后才是将前继节点的next指向当前节点。

4. AQS为什么用双向链表

线程在排队期间是可以取消的,取消之后需要将前继节点的next指向后继节点。如果是单向链表,只能找到前继或后继节点中的一个,另一个需要遍历整个链表,效率低。所以采用双向链表结构。

5. 对象头中的指针压缩是什么?

ClassPoint只占用了4个字节,正常来说只能支持4G的寻址,但通过指针压缩,可以支持32G的寻址。

image.png

6. 公平锁和非公平锁的底层实现

synchronized只有非公平锁,而ReentrantLock有公平锁和非公平锁。
公平锁:

  • lock方法直接执行tryAcquire。
  • tryAcquire方法:如果state为0,先查看AQS中是否有排队的线程,如果有并且当前线程不是head的next位置,就去排队;否则尝试CAS将state从0改为1。

非公平锁:

  • lock方法直接执行CAS尝试将state从0改为1,如果失败再执行tryAcquire。
  • tryAcquire方法直接尝试CAS将state从0改为1。

7. ConcurrentHashMap的计数器实现

计数器用于记录元素个数,CHM采用LongAdder作为计数器的实现,但并没有直接引用LongAdder,而是仿照LongAdder源码实现了一份,分段存储元素个数。

8. ConcurrentHashMap的sizeCtl代表什么?

sizeCtl是用来控制数据操作的。

  • sizeCtl > 0:可能是下次扩容的阈值或初始化数组的长度。
  • sizeCtl == 0:数组还没初始化,默认长度为16。
  • sizeCtl == -1:数组正在初始化。
  • sizeCtl < -1:数组正在扩容。

image.png

image.png

9. ConcurrentHashMap的BUG

协助扩容的条件判断BUG:

if (check >= 0) {
   
    Node<K,V>[] tab, nt; int n, sc;
    while (s >= 
  • 34
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖的小熊猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值