|
31 | 31 | - [ReentrantReadWriteLock](#reentrantreadwritelock) |
32 | 32 | - [要点](#要点-3) |
33 | 33 | - [示例](#示例-1) |
| 34 | +- [AQS](#aqs) |
| 35 | + - [要点](#要点-4) |
| 36 | + - [源码](#源码-3) |
| 37 | + - [独占锁](#独占锁) |
34 | 38 | - [资料](#资料) |
35 | 39 |
|
36 | 40 | <!-- /TOC --> |
@@ -177,13 +181,15 @@ public class ReentrantLockDemo { |
177 | 181 |
|
178 | 182 | ### 要点 |
179 | 183 |
|
180 | | -* 功能 |
181 | | - * 对于特定的资源,ReadWriteLock 允许多个线程同时对其执行读操作,但是只允许一个线程对其执行写操作。 |
182 | | -* 原理 |
183 | | - * “读-读”线程之间不存在互斥关系。 |
184 | | - * “读-写”线程、“写-写”线程之间存在互斥关系。 |
185 | | - * ReadWriteLock 维护一对相关的锁。一个是读锁;一个是写锁。 |
186 | | - * 将读写锁分开,有利于提高并发效率。 |
| 184 | +作用:对于特定的资源,ReadWriteLock 允许多个线程同时对其执行读操作,但是只允许一个线程对其执行写操作。 |
| 185 | + |
| 186 | +原理 |
| 187 | + |
| 188 | +“读-读”线程之间不存在互斥关系。 |
| 189 | + |
| 190 | +“读-写”线程、“写-写”线程之间存在互斥关系。 |
| 191 | + |
| 192 | +ReadWriteLock 维护一对相关的锁。一个是读锁;一个是写锁。将读写锁分开,有利于提高并发效率。 |
187 | 193 |
|
188 | 194 | <p align="center"> |
189 | 195 | <img src="https://raw.githubusercontent.com/dunwu/javase-notes/master/images/concurrent/ReadWriteLock.jpg"> |
@@ -211,8 +217,7 @@ public interface ReadWriteLock { |
211 | 217 |
|
212 | 218 | ### 要点 |
213 | 219 |
|
214 | | -* 功能 |
215 | | - * ReentrantReadWriteLock 实现了 ReadWriteLock 接口,所以它是一个读写锁。 |
| 220 | +作用:ReentrantReadWriteLock 实现了 ReadWriteLock 接口,所以它是一个读写锁。 |
216 | 221 |
|
217 | 222 | ### 示例 |
218 | 223 |
|
@@ -243,8 +248,92 @@ public class ReentrantReadWriteLockDemo { |
243 | 248 | } |
244 | 249 | ``` |
245 | 250 |
|
| 251 | +## AQS |
| 252 | + |
| 253 | +### 要点 |
| 254 | + |
| 255 | +作用:AQS,AbstractQueuedSynchronizer,即队列同步器。它是构建锁或者其他同步组件的基础框架(如 ReentrantLock、ReentrantReadWriteLock、Semaphore 等)。 |
| 256 | + |
| 257 | +场景:在 LOCK 包中的相关锁(常用的有 ReentrantLock、 ReadWriteLock)都是基于 AQS 来构建。然而这些锁都没有直接来继承 AQS,而是定义了一个 Sync 类去继承 AQS。那么为什么要这样呢?because:锁面向的是使用用户,而同步器面向的则是线程控制,那么在锁的实现中聚合同步器而不是直接继承 AQS 就可以很好的隔离二者所关注的事情。 |
| 258 | + |
| 259 | +原理:AQS 在内部定义了一个 int 变量 state,用来表示同步状态。AQS 通过一个双向的 FIFO 同步队列来完成同步状态的管理,当有线程获取锁失败后,就被添加到队列末尾。 |
| 260 | + |
| 261 | +### 源码 |
| 262 | + |
| 263 | +AbstractQueuedSynchronizer 继承自 AbstractOwnableSynchronize。 |
| 264 | + |
| 265 | +#### 同步队列 |
| 266 | + |
| 267 | +```java |
| 268 | +public abstract class AbstractQueuedSynchronizer |
| 269 | + extends AbstractOwnableSynchronizer |
| 270 | + implements java.io.Serializable { |
| 271 | + |
| 272 | + /** 等待队列的队头,懒加载。只能通过 setHead 方法修改。 */ |
| 273 | + private transient volatile Node head; |
| 274 | + /** 等待队列的队尾,懒加载。只能通过 enq 方法添加新的等待节点。*/ |
| 275 | + private transient volatile Node tail; |
| 276 | + /** 同步状态 */ |
| 277 | + private volatile int state; |
| 278 | +} |
| 279 | +``` |
| 280 | + |
| 281 | +<p align="center"> |
| 282 | + <img src="http://www.liuhaihua.cn/wp-content/uploads/2018/05/7zei6fI.png"> |
| 283 | +</p> |
| 284 | + |
| 285 | +##### Node |
| 286 | + |
| 287 | +```java |
| 288 | +static final class Node { |
| 289 | + /** 该等待同步的节点处于共享模式 */ |
| 290 | + static final Node SHARED = new Node(); |
| 291 | + /** 该等待同步的节点处于独占模式 */ |
| 292 | + static final Node EXCLUSIVE = null; |
| 293 | + |
| 294 | + /** 等待状态,这个和 state 是不一样的:有 1,0,-1,-2,-3 五个值 */ |
| 295 | + volatile int waitStatus; |
| 296 | + static final int CANCELLED = 1; |
| 297 | + static final int SIGNAL = -1; |
| 298 | + static final int CONDITION = -2; |
| 299 | + static final int PROPAGATE = -3; |
| 300 | + |
| 301 | + /** 前驱节点 */ |
| 302 | + volatile Node prev; |
| 303 | + /** 后继节点 */ |
| 304 | + volatile Node next; |
| 305 | + /** 等待锁的线程 */ |
| 306 | + volatile Thread thread; |
| 307 | +} |
| 308 | +``` |
| 309 | + |
| 310 | +很显然,Node 是一个双链表结构。 |
| 311 | + |
| 312 | +waitStatus 5 个状态值的含义: |
| 313 | + |
| 314 | +1. CANCELLED(1) - 该节点的线程可能由于超时或被中断而处于被取消(作废)状态,一旦处于这个状态,节点状态将一直处于 CANCELLED(作废),因此应该从队列中移除. |
| 315 | +2. SIGNAL(-1) - 当前节点为 SIGNAL 时,后继节点会被挂起,因此在当前节点释放锁或被取消之后必须被唤醒(unparking)其后继结点. |
| 316 | +3. CONDITION(-2) - 该节点的线程处于等待条件状态,不会被当作是同步队列上的节点,直到被唤醒(signal),设置其值为 0,重新进入阻塞状态。 |
| 317 | +4. PROPAGATE(-3) - 下一个 acquireShared 应无条件传播。 |
| 318 | +5. 0 - 非以上状态。 |
| 319 | + |
| 320 | +```java |
| 321 | +public final void acquire(int arg) { |
| 322 | + if (!tryAcquire(arg) && |
| 323 | + acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) |
| 324 | + selfInterrupt(); |
| 325 | +} |
| 326 | +``` |
| 327 | + |
| 328 | +### 独占锁 |
| 329 | + |
| 330 | +独占锁的获取(acquire方法) |
| 331 | + |
246 | 332 | ## 资料 |
247 | 333 |
|
248 | 334 | * [Java 并发编程实战](https://item.jd.com/10922250.html) |
249 | 335 | * [Java 并发编程的艺术](https://item.jd.com/11740734.html) |
250 | 336 | * http://www.cnblogs.com/dolphin0520/p/3923167.html |
| 337 | +* https://zhuanlan.zhihu.com/p/27134110 |
| 338 | +* https://t.hao0.me/java/2016/04/01/aqs.html |
| 339 | +* http://ju.outofmemory.cn/entry/353762 |
0 commit comments