@@ -622,7 +622,7 @@ t.start();
622622| public final void suspend() | **挂起(暂停)线程运行** |
623623| public final void resume() | 恢复线程运行 |
624624
625- 所以 Java 中线程的状态是阻塞,很少使用挂起
625+
626626
627627
628628
@@ -9289,7 +9289,7 @@ ReentrantReadWriteLock 其**读锁是共享锁,写锁是独占锁**
92899289
92909290* **重入时升级不支持**:持有读锁的情况下去获取写锁会导致获取写锁永久等待,需要先释放读,再去获得写
92919291
9292- * **重入时降级支持**:持有写锁的情况下去获取读锁
9292+ * **重入时降级支持**:持有写锁的情况下去获取读锁,造成只有当前线程会持有读锁,因为写锁会互斥其他的锁
92939293
92949294 ```java
92959295 w.lock();
@@ -9357,7 +9357,7 @@ public static void main(String[] args) {
93579357
93589358缓存更新时,是先清缓存还是先更新数据库
93599359
9360- * 先清缓存:可能造成刚清理缓存还没有更新数据库,线程直接查询了数据库更新缓存
9360+ * 先清缓存:可能造成刚清理缓存还没有更新数据库,线程直接查询了数据库更新过期数据到缓存
93619361
93629362* 先更新据库:可能造成刚更新数据库,还没清空缓存就有线程从缓存拿到了旧数据
93639363
@@ -9375,14 +9375,110 @@ public static void main(String[] args) {
93759375
93769376#### 实现原理
93779377
9378- ##### 加锁原理
9378+ ##### 成员属性
93799379
93809380读写锁用的是同一个 Sycn 同步器,因此等待队列、state 等也是同一个,原理与 ReentrantLock 加锁相比没有特殊之处,不同是**写锁状态占了 state 的低 16 位,而读锁使用的是 state 的高 16 位**
93819381
9382- * t1 w.lock(写锁),成功上锁 state = 0_1
9382+ * 读写锁:
9383+
9384+ ```java
9385+ private final ReentrantReadWriteLock.ReadLock readerLock;
9386+ private final ReentrantReadWriteLock.WriteLock writerLock;
9387+ ```
9388+
9389+ * 构造方法:默认是非公平锁,可以指定参数创建公平锁
9390+
9391+ ```java
9392+ public ReentrantReadWriteLock(boolean fair) {
9393+ // true 为公平锁
9394+ sync = fair ? new FairSync() : new NonfairSync();
9395+ // 这两个 lock 共享同一个 sync 实例,都是由 ReentrantReadWriteLock 的 sync 提供同步实现
9396+ readerLock = new ReadLock(this);
9397+ writerLock = new WriteLock(this);
9398+ }
9399+ ```
9400+
9401+ Sync 类的属性:
9402+
9403+ * 统计变量:
9404+
9405+ ```java
9406+ // 用来移位
9407+ static final int SHARED_SHIFT = 16;
9408+ // 高16位的1
9409+ static final int SHARED_UNIT = (1 << SHARED_SHIFT);
9410+ // 65535,16个1,代表写锁的最大重入次数
9411+ static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
9412+ // 低16位掩码:0b 1111 1111 1111 1111,用来获取写锁重入的次数
9413+ static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
9414+ ```
9415+
9416+ * 获取读写锁的次数:
9417+
9418+ ```java
9419+ // 获取读写锁的读锁分配的总次数
9420+ static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
9421+ // 写锁(独占)锁的重入次数
9422+ static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
9423+ ```
9424+
9425+ * 内部类:
9426+
9427+ ```java
9428+ // 记录读锁线程自己的持有读锁的数量(重入次数),因为 state 高16位记录的是全局范围内所有的读线程获取读锁的总量
9429+ static final class HoldCounter {
9430+ int count = 0;
9431+ // Use id, not reference, to avoid garbage retention
9432+ final long tid = getThreadId(Thread.currentThread());
9433+ }
9434+ // 线程安全的存放线程各自的 HoldCounter 对象
9435+ static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> {
9436+ public HoldCounter initialValue() {
9437+ return new HoldCounter();
9438+ }
9439+ }
9440+ ```
9441+
9442+ * 内部类实例:
9443+
9444+ ```java
9445+ // 当前线程持有的可重入读锁的数量,计数为 0 时删除
9446+ private transient ThreadLocalHoldCounter readHolds;
9447+ // 记录最后一个获取【读锁】线程的 HoldCounter 对象
9448+ private transient HoldCounter cachedHoldCounter;
9449+ ```
9450+
9451+ * 首次获取锁:
9452+
9453+ ```java
9454+ // 第一个获取读锁的线程
9455+ private transient Thread firstReader = null;
9456+ // 记录该线程持有的读锁次数(读锁重入次数)
9457+ private transient int firstReaderHoldCount;
9458+ ```
9459+
9460+ * Sync 构造方法:
93839461
93849462 ```java
9385- //lock() -> sync.acquire(1);
9463+ Sync() {
9464+ readHolds = new ThreadLocalHoldCounter();
9465+ // 确保其他线程的数据可见性,state 是 volatile 修饰的变量,重写该值会将线程本地缓存数据【同步至主存】
9466+ setState(getState());
9467+ }
9468+ ```
9469+
9470+
9471+
9472+ ***
9473+
9474+
9475+
9476+ ##### 加锁原理
9477+
9478+ * t1 线程:w.lock(**写锁**),成功上锁 state = 0_1
9479+
9480+ ```java
9481+ // lock() -> sync.acquire(1);
93869482 public void lock() {
93879483 sync.acquire(1);
93889484 }
@@ -9399,20 +9495,22 @@ public static void main(String[] args) {
93999495 int c = getState();
94009496 // 获得低 16 位, 代表写锁的 state 计数
94019497 int w = exclusiveCount(c);
9498+ // 说明有读锁或者写锁
94029499 if (c != 0) {
9403- // c != 0 and w == 0 表示 r != 0,有读锁, 读锁不能升级,直接返回false
9500+ // c != 0 and w == 0 表示有读锁,【 读锁不能升级】,直接返回 false
94049501 // w != 0 说明有写锁,写锁的拥有者不是自己,获取失败
94059502 if (w == 0 || current != getExclusiveOwnerThread())
94069503 return false;
9407- // 锁重入计数超过低 16 位, 报异常
9504+
9505+ // 执行到这里只有一种情况:【写锁重入】,所以下面几行代码不存在并发
94089506 if (w + exclusiveCount(acquires) > MAX_COUNT)
94099507 throw new Error("Maximum lock count exceeded");
9410- // 【 写锁重入, 获得锁成功】
9508+ // 写锁重入, 获得锁成功,没有并发,所以不使用 CAS
94119509 setState(c + acquires);
94129510 return true;
94139511 }
94149512
9415- // c == 0,没有任何锁 ,判断写锁是否该阻塞,是 false 就尝试获取锁,失败返回 false
9513+ // c == 0,说明没有任何锁 ,判断写锁是否该阻塞,是 false 就尝试获取锁,失败返回 false
94169514 if (writerShouldBlock() || !compareAndSetState(c, c + acquires))
94179515 return false;
94189516 // 获得锁成功,设置锁的持有线程为当前线程
@@ -9429,11 +9527,11 @@ public static void main(String[] args) {
94299527 }
94309528 ```
94319529
9432- * t2 r.lock(读锁 ),进入 tryAcquireShared 流程,如果有写锁占据,那么 tryAcquireShared
9530+ * t2 r.lock(**读锁** ),进入 tryAcquireShared 流程:
94339531
94349532 * 返回 -1 表示失败
94359533 * 如果返回 0 表示成功
9436- * 返回正数表示还有多少后继节点支持共享模式,读写锁返回1
9534+ * 返回正数表示还有多少后继节点支持共享模式,读写锁返回 1
94379535
94389536 ```java
94399537 public void lock() {
@@ -9451,28 +9549,113 @@ public static void main(String[] args) {
94519549 protected final int tryAcquireShared(int unused) {
94529550 Thread current = Thread.currentThread();
94539551 int c = getState();
9454- // 低 16 位, 代表写锁的 state
9455- // 如果是其它线程持有写锁, 并且写锁的持有者不是当前线程,获取读锁失败 ,【写锁允许降级】
9552+ // exclusiveCount(c) 代表低 16 位, 写锁的 state,成立说明有线程持有写锁
9553+ // 写锁的持有者不是当前线程,则获取读锁失败 ,【写锁允许降级】
94569554 if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current)
94579555 return -1;
94589556
9459- // 高 16 位,代表读锁的 state
9557+ // 高 16 位,代表读锁的 state,共享锁分配出去的总次数
94609558 int r = sharedCount(c);
9461- if (!readerShouldBlock() && // 读锁不该阻塞
9462- r < MAX_COUNT && // 小于读锁计数
9463- compareAndSetState(c, c + SHARED_UNIT)) {// 尝试增加计数成功
9464- // ....
9559+ // 读锁是否应该阻塞
9560+ if (!readerShouldBlock() && r < MAX_COUNT &&
9561+ compareAndSetState(c, c + SHARED_UNIT)) { // 尝试增加读锁计数
9562+ // 加锁成功
9563+ // 加锁之前读锁为 0,说明当前线程是第一个读锁线程
9564+ if (r == 0) {
9565+ firstReader = current;
9566+ firstReaderHoldCount = 1;
9567+ // 第一个读锁线程是自己就发生了读锁重入
9568+ } else if (firstReader == current) {
9569+ firstReaderHoldCount++;
9570+ } else {
9571+ // cachedHoldCounter 设置为当前线程的 holdCounter 对象,即最后一个获取读锁的线程
9572+ HoldCounter rh = cachedHoldCounter;
9573+ // 说明还没设置 rh
9574+ if (rh == null || rh.tid != getThreadId(current))
9575+ // 获取当前线程的锁重入的对象,赋值给 cachedHoldCounter
9576+ cachedHoldCounter = rh = readHolds.get();
9577+ // 还没重入
9578+ else if (rh.count == 0)
9579+ readHolds.set(rh);
9580+ // 重入 + 1
9581+ rh.count++;
9582+ }
94659583 // 读锁加锁成功
94669584 return 1;
94679585 }
9468- // 与 tryAcquireShared 功能类似, 但会不断尝试 for (;;) 获取读锁, 执行过程中无阻塞
9586+ // 逻辑到这 应该阻塞,或者 cas 加锁失败
9587+ // 会不断尝试 for (;;) 获取读锁, 执行过程中无阻塞
94699588 return fullTryAcquireShared(current);
94709589 }
9471- // 非公平锁 readerShouldBlock 看 AQS 队列中第一个节点是否是写锁,是则阻塞,反之不阻塞
9590+ // 非公平锁 readerShouldBlock 偏向写锁一些,看 AQS 阻塞队列中第一个节点是否是写锁,是则阻塞,反之不阻塞
9591+ // 防止一直有读锁线程,导致写锁线程饥饿
94729592 // true 则该阻塞, false 则不阻塞
94739593 final boolean readerShouldBlock() {
94749594 return apparentlyFirstQueuedIsExclusive();
94759595 }
9596+ final boolean readerShouldBlock() {
9597+ return hasQueuedPredecessors();
9598+ }
9599+ ```
9600+
9601+ ```java
9602+ final int fullTryAcquireShared(Thread current) {
9603+ // 当前读锁线程持有的读锁次数对象
9604+ HoldCounter rh = null;
9605+ for (;;) {
9606+ int c = getState();
9607+ // 说明有线程持有写锁
9608+ if (exclusiveCount(c) != 0) {
9609+ // 写锁不是自己则获取锁失败
9610+ if (getExclusiveOwnerThread() != current)
9611+ return -1;
9612+ } else if (readerShouldBlock()) {
9613+ // 条件成立说明当前线程是 firstReader,当前锁是读忙碌状态,而且当前线程也是读锁重入
9614+ if (firstReader == current) {
9615+ // assert firstReaderHoldCount > 0;
9616+ } else {
9617+ if (rh == null) {
9618+ // 最后一个读锁的 HoldCounter
9619+ rh = cachedHoldCounter;
9620+ // 说明当前线程也不是最后一个读锁
9621+ if (rh == null || rh.tid != getThreadId(current)) {
9622+ // 获取当前线程的 HoldCounter
9623+ rh = readHolds.get();
9624+ // 条件成立说明 HoldCounter 对象是上一步代码新建的
9625+ // 当前线程不是锁重入,在 readerShouldBlock() 返回 true 时需要去排队
9626+ if (rh.count == 0)
9627+ // 防止内存泄漏
9628+ readHolds.remove();
9629+ }
9630+ }
9631+ if (rh.count == 0)
9632+ return -1;
9633+ }
9634+ }
9635+ // 越界判断
9636+ if (sharedCount(c) == MAX_COUNT)
9637+ throw new Error("Maximum lock count exceeded");
9638+ // 读锁加锁,条件内的逻辑与 tryAcquireShared 相同
9639+ if (compareAndSetState(c, c + SHARED_UNIT)) {
9640+ if (sharedCount(c) == 0) {
9641+ firstReader = current;
9642+ firstReaderHoldCount = 1;
9643+ } else if (firstReader == current) {
9644+ firstReaderHoldCount++;
9645+ } else {
9646+ if (rh == null)
9647+ rh = cachedHoldCounter;
9648+ if (rh == null || rh.tid != getThreadId(current))
9649+ rh = readHolds.get();
9650+ else if (rh.count == 0)
9651+ readHolds.set(rh);
9652+ rh.count++;
9653+ cachedHoldCounter = rh; // cache for release
9654+ }
9655+ return 1;
9656+ }
9657+ }
9658+ }
94769659 ```
94779660
94789661* 获取读锁失败,进入 sync.doAcquireShared(1) 流程开始阻塞,首先也是调用 addWaiter 添加节点,不同之处在于节点被设置为 Node.SHARED 模式而非 Node.EXCLUSIVE 模式,注意此时 t2 仍处于活跃状态
@@ -9603,7 +9786,7 @@ public static void main(String[] args) {
96039786 else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
96049787 continue;
96059788 }
9606- // 条件不成立说明被唤醒的节点非常积极,直接将自己设置为了新的head ,
9789+ // 条件不成立说明被唤醒的节点非常积极,直接将自己设置为了新的 head ,
96079790 // 此时唤醒它的节点(前驱)执行 h == head 不成立,所以不会跳出循环,会继续唤醒新的 head 节点的后继节点
96089791 if (h == head)
96099792 break;
@@ -9632,7 +9815,7 @@ public static void main(String[] args) {
96329815
96339816 ```java
96349817 protected final boolean tryReleaseShared(int unused) {
9635- //
9818+
96369819 for (;;) {
96379820 int c = getState();
96389821 int nextc = c - SHARED_UNIT;
0 commit comments