@@ -22,20 +22,20 @@ AQS 为构建锁和同步器提供了一些通用功能的是实现,因此,
2222
2323## AQS 原理
2424
25- > 👍推荐阅读:[ 从 ReentrantLock 的实现看 AQS 的原理及应用] ( ./reentrantlock.md )
26-
2725在面试中被问到并发知识的时候,大多都会被问到“请你说一下自己对于 AQS 原理的理解”。下面给大家一个示例供大家参考,面试不是背题,大家一定要加入自己的思想,即使加入不了自己的思想也要保证自己能够通俗的讲出来而不是背出来。
2826
2927### AQS 核心思想
3028
31- AQS 核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制 AQS 是用 ** CLH 队列锁 ** 实现的,即将暂时获取不到锁的线程加入到队列中 。
29+ AQS 核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制 AQS 是基于 ** CLH 锁 ** (Craig, Landin, and Hagersten locks) 实现的。
3230
33- CLH(Craig,Landin,and Hagersten) 队列是一个虚拟的双向队列 (虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS 是将每条请求共享资源的线程封装成一个 CLH 锁队列的一个结点 (Node)来实现锁的分配。在 CLH 同步队列中 ,一个节点表示一个线程,它保存着线程的引用(thread)、 当前节点在队列中的状态(waitStatus)、前驱节点(prev)、后继节点(next)。
31+ CLH 锁是对自旋锁的一种改进,是一个虚拟的双向队列 (虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系),暂时获取不到锁的线程将被加入到该队列中 。AQS 将每条请求共享资源的线程封装成一个 CLH 队列锁的一个结点 (Node)来实现锁的分配。在 CLH 队列锁中 ,一个节点表示一个线程,它保存着线程的引用(thread)、 当前节点在队列中的状态(waitStatus)、前驱节点(prev)、后继节点(next)。
3432
35- CLH 队列结构如下图所示 :
33+ CLH 队列锁结构如下图所示 :
3634
3735![ ] ( https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/40cb932a64694262993907ebda6a0bfe~tplv-k3u1fbpfcp-zoom-1.image )
3836
37+ 关于AQS 核心数据结构-CLH 锁的详细解读,强烈推荐阅读 [ Java AQS 核心数据结构-CLH 锁 - Qunar技术沙龙] ( https://mp.weixin.qq.com/s/jEx-4XhNGOFdCo4Nou5tqg ) 这篇文章。
38+
3939AQS(` AbstractQueuedSynchronizer ` )的核心原理图(图源[ Java 并发之 AQS 详解] ( https://www.cnblogs.com/waterystone/p/4920797.html ) )如下:
4040
4141![ ] ( https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/Java%20%E7%A8%8B%E5%BA%8F%E5%91%98%E5%BF%85%E5%A4%87%EF%BC%9A%E5%B9%B6%E5%8F%91%E7%9F%A5%E8%AF%86%E7%B3%BB%E7%BB%9F%E6%80%BB%E7%BB%93/CLH.png )
@@ -66,7 +66,7 @@ protected final boolean compareAndSetState(int expect, int update) {
6666}
6767```
6868
69- 以 ` ReentrantLock ` 为例,` state ` 初始值为 0,表示未锁定状态。A 线程 ` lock() ` 时,会调用 ` tryAcquire() ` 独占该锁并将 ` state+1 ` 。此后,其他线程再 ` tryAcquire() ` 时就会失败,直到 A 线程 ` unlock() ` 到 ` state= ` 0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A 线程自己是可以重复获取此锁的(` state ` 会累加),这就是可重入的概念。但要注意,获取多少次就要释放多少次,这样才能保证 state 是能回到零态的。
69+ 以 ` ReentrantLock ` 为例,` state ` 初始值为 0,表示未锁定状态。A 线程 ` lock() ` 时,会调用 ` tryAcquire() ` 独占该锁并将 ` state+1 ` 。此后,其他线程再 ` tryAcquire() ` 时就会失败,直到 A 线程 ` unlock() ` 到 ` state= ` 0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A 线程自己是可以重复获取此锁的(` state ` 会累加),这就是可重入的概念。但要注意,获取多少次就要释放多少次,这样才能保证 state 是能回到零态的。相关阅读: [ 从 ReentrantLock 的实现看 AQS 的原理及应用 - 美团技术团队 ] ( ./reentrantlock.md ) 。
7070
7171再以 ` CountDownLatch ` 以例,任务分为 N 个子线程去执行,` state ` 也初始化为 N(注意 N 要与线程个数一致)。这 N 个子线程是并行执行的,每个子线程执行完后` countDown() ` 一次,state 会 CAS(Compare and Swap) 减 1。等到所有子线程都执行完后(即 ` state=0 ` ),会 ` unpark() ` 主调用线程,然后主调用线程就会从 ` await() ` 函数返回,继续后余动作。
7272
0 commit comments