ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

YCBlogs 并发深入:ReentrantLock 锁全解析——重入性、公平性、可中断与 AQS 源码原理

YCBlogs 并发深入:ReentrantLock 锁全解析——重入性、公平性、可中断与 AQS 源码原理 教程技术博客文档【免费下载链接】YCBlogs技术博客笔记大汇总包括Java基础线程并发数据结构Android技术博客等等常用设计模式常见的算法网络协议知识点部分flutter笔记还包括平时开发中遇到的bug汇总当然也在工作之余收集了大量的面试题长期更新维护并且修正持续完善……开源的文件是markdown格式的转载请注明出处谢谢项目地址https://gitcode.com/gh_mirrors/yc/YCBlogs点击查看免费下载本篇是 YCBlogs 仓库 java/07.Java并发 系列中的核心一篇围绕java.util.concurrent包下的互斥锁 ReentrantLock 展开从它是什么、比 synchronized 强在哪讲起给出可直接复制的标准使用模板与公平/非公平锁实测案例并深入到Sync、FairSync、NonfairSync、Condition的源码级别解释重入计数、hasQueuedPredecessors排队判断和条件队列的完整语义。读完你既能掌握 ReentrantLock 的正确打开方式lock try/finally unlock也能说清公平锁为何要用同步队列的第一个节点、重入锁为何要释放 n 次才真正解锁以及它与 synchronized 究竟该怎么选。01. ReentrantLock 是什么比 synchronized 多出的三项高级能力ReentrantLock 是java.util.concurrent包下提供的一套互斥锁。它实现了Lock接口与synchronized关键字相比ReentrantLock类提供了三项高级功能详见原文档 06.ReentrantLock锁.md等待可中断当持有锁的线程长期不释放锁时正在等待的线程可以选择放弃等待、转而处理其他事情这在某种程度上可以避免死锁。synchronized做不到这一点——等待中的线程只能一直阻塞。公平锁多个线程等待同一个锁时必须按照申请锁的时间顺序获得锁。synchronized是非公平锁ReentrantLock默认构造的也是非公平锁但可以通过构造参数true显式开启公平锁。需要注意公平锁为了保证先来先得的绝对顺序性能表现通常不如非公平锁。锁绑定多个条件一个ReentrantLock对象可以同时绑定多个Condition对象实现一把锁 多组等待队列的精细协作而synchronized的wait/notify只能绑定一个隐含条件。如果放到整个锁体系里看ReentrantLock 属于仓库 03.锁一些基础概念.md 中归纳的悲观锁、可重入锁、独享锁、可中断锁这一类它假定共享数据一定会被并发修改所以通过显式加锁来保证线程安全同一线程可重复获取同一时刻只允许一个线程持有并支持响应中断的获取方式。02. ReentrantLock 使用方法标准模板与注意事项2.1 最小可用代码原文档给出了最基础的使用方式核心是lock() 与 unlock() 必须成对出现且解锁动作一定要放在finally中保证无论业务代码是否抛异常锁都能被释放private ReentrantLock lock new ReentrantLock(); public void run() { lock.lock(); try { for (int i 0; i 5; i) { System.out.println(Thread.currentThread().getName() : i); } } finally { lock.unlock(); } }2.2 三个必须遵守的纪律每个lock()动作都建议立即对应一个try-catch-finally原文档原文强调为保证锁释放每一个 lock() 动作建议都立即对应一个 try-catch-finally。不要将获取锁的过程写在try块内如果在获取锁时发生了异常异常抛出的同时会让人误以为锁被释放了实际上锁根本未被持有容易掩盖真实问题。正确姿势是先lock.lock()再进入try。在finally中释放锁这是使用ReentrantLock时最重要的原则目的就是保证获取锁之后最终一定能被释放。synchronized发生异常时 JVM 会自动释放锁而Lock不会忘记unlock()极可能造成死锁。这两条纪律在仓库的对比文档 08.两种不同锁区别.md 第 06 节注意事项中同样被重点强调属于使用 ReentrantLock 的第一道安全底线。2.3 从源码看 lock 与 unlock 的委托关系ReentrantLock本身并没有直接实现锁竞争逻辑而是通过组合模式把lock/unlock委托给内部同步器Sync完成完整类结构见 07.ReentrantLock原理.mdpublic class ReentrantLock implements Lock, java.io.Serializable { private final Sync sync; // 组合一个 Sync 同步器 public void lock() { sync.lock(); // 委托给 Sync 的子类实现 } public void unlock() { sync.release(1); // 通过 AQS 的 release 释放 } public Condition newCondition() { return sync.newCondition(); // 创建绑定该锁的条件变量 } }其中abstract static class Sync extends AbstractQueuedSynchronizerAQS。也就是说ReentrantLock 的全部同步语义获取、释放、排队、重入计数都是通过重写 AQS 的若干 protected 方法来表达的——这正是后面第 05、06 节源码分析的入口。Doug Lea 正是用这种组合 委托的方式把易用性面向用户的 Lock API与灵活性面向同步器的 AQS 扩展点解耦开。03. ReentrantLock 锁机制测试验证互斥效果原文档提供了一个完整的测试两个线程t1、t2共享同一个Runnable实例从而共享同一把锁观察输出可以直观验证互斥性private void test2() { Runnable t1 new MyThread(); new Thread(t1, t1).start(); new Thread(t1, t2).start(); } class MyThread implements Runnable { private ReentrantLock lock new ReentrantLock(); public void run() { lock.lock(); try { for (int i 0; i 5; i) { System.out.println(Thread.currentThread().getName() : i); } } finally { lock.unlock(); } } }运行结果原文档实测日志10-17 17:06:59.222 6531-6846/com.yc.cn.ycbaseadapter I/System.out: t1:0 10-17 17:06:59.222 6531-6846/com.yc.cn.ycbaseadapter I/System.out: t1:1 10-17 17:06:59.222 6531-6846/com.yc.cn.ycbaseadapter I/System.out: t1:2 10-17 17:06:59.222 6531-6846/com.yc.cn.ycbaseadapter I/System.out: t1:3 10-17 17:06:59.222 6531-6846/com.yc.cn.ycbaseadapter I/System.out: t1:4 10-17 17:06:59.224 6531-6847/com.yc.cn.ycbaseadapter I/System.out: t2:0 10-17 17:06:59.225 6531-6847/com.yc.cn.ycbaseadapter I/System.out: t2:1 10-17 17:06:59.225 6531-6847/com.yc.cn.ycbaseadapter I/System.out: t2:2 10-17 17:06:59.225 6531-6847/com.yc.cn.ycbaseadapter I/System.out: t2:3 10-17 17:06:59.225 6531-6847/com.yc.cn.ycbaseadapter I/System.out: t2:4可以看到t1完整打印完 04 之后t2才获得执行机会——因为两线程竞争同一把ReentrantLockt1持锁期间t2只能在 AQS 同步队列中阻塞等待t1调用unlock()内部走sync.release(1)释放后t2才被唤醒并重新竞争锁。这也印证了第 02 节lock/unlock 必须成对、finally 兜底的必要性一旦持锁线程异常退出且没有释放锁t2将永远阻塞。04. 何时用 ReentrantLock场景判断与选型建议4.1 适用场景原文档明确给出的适用场景是时间锁等候带超时的锁获取可中断锁等候等待可被中断无块结构锁锁的获取与释放跨越多个代码块/方法多个条件变量一把锁绑定多个 Condition锁投票概括一句话在确实需要一些 synchronized 所没有的特性的时候才考虑使用 ReentrantLock。4.2 如何选用先用 synchronized直到证明它不合适原文档的选型建议非常务实核心观点是ReentrantLock 具有可伸缩性的好处应当在高度争用的情况下使用它但大多数 synchronized 块几乎从来没有出现过争用。建议用 synchronized 开发直到确实证明 synchronized 不合适而不要仅仅假设使用 ReentrantLock性能会更好。这些是供高级用户使用的高级工具首先要把事情做好然后再考虑是否有必要做得更快。这一点在仓库 08.两种不同锁区别.md 中也有完全一致的表述且补充了性能背景JDK 1.5 时代 synchronized 是重量级操作挂起/恢复线程需要用户态与内核态切换性能压力大到 JDK 1.6JVM 引入了自适应自旋、锁消除、锁粗化、轻量级锁、偏向锁等优化详见 05.Synchronize原理.md 的锁升级机制讲解synchronized 的性能已不比 Lock 差官方也更倾向 synchronized并在未来版本持续优化它。因此能用 synchronized 满足需求时优先用 synchronized。4.3 真实工业案例ThreadPoolExecutor 中的 mainLock原文档特意摘取了ThreadPoolExecutor.addWorkerFailed方法作为 ReentrantLock 的工业级使用范例说明这个类中很多地方用到了这个锁/** * Rolls back the worker thread creation. * - removes worker from workers, if present * - decrements worker count * - rechecks for termination, in case the existence of this * worker was holding up termination */ private void addWorkerFailed(Worker w) { final ReentrantLock mainLock this.mainLock; mainLock.lock(); try { if (w ! null) workers.remove(w); decrementWorkerCount(); tryTerminate(); } finally { mainLock.unlock(); } }这个例子至少体现三点实战要点其一先把mainLock存入局部变量减少一次字段读取其二lock()在try之外其三unlock()严格放在finally中。线程池对内部状态workers集合、worker 计数的并发保护正是借助这把 ReentrantLock 完成的是无块结构锁 高争用内部状态保护的典型场景。05. 公平锁与非公平锁从构造器到 hasQueuedPredecessors5.1 什么是公平性公平性是指在竞争场景中当公平性为真时锁会倾向于赋予等待时间最久的线程它是减少线程饥饿个别线程长期等待锁却始终无法获取的办法公平锁老的线程排队使用锁新线程仍然排队使用锁——严格按申请顺序来非公平锁老的线程排队使用锁但无法保证新线程不抢占已经排队的线程的锁。何谓公平性是针对获取锁而言的如果一个锁是公平的那么锁的获取顺序应该符合请求上的绝对时间顺序满足FIFO。5.2 两个构造方法对应的源码// 无参构造默认非公平锁 public ReentrantLock() { sync new NonfairSync(); } // 带 boolean 构造true 为公平锁false 为非公平锁 public ReentrantLock(boolean fair) { sync fair ? new FairSync() : new NonfairSync(); }FairSync与NonfairSync都是Sync的子类各自重写了lock()与tryAcquire(int)从而表达不同的同步语义。5.3 公平锁的获取逻辑多了一个 hasQueuedPredecessors原文档给出的公平锁核心方法protected final boolean tryAcquire(int acquires) { final Thread current Thread.currentThread(); int c getState(); if (c 0) { if (!hasQueuedPredecessors() compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current getExclusiveOwnerThread()) { int nextc c acquires; if (nextc 0) throw new Error(Maximum lock count exceeded); setState(nextc); return true; } return false; }这段逻辑与nonfairTryAcquire见第 06 节几乎一致唯一的不同在于增加了hasQueuedPredecessors()判断该方法用于判断当前节点在同步队列中是否有前驱节点——如果有说明有线程比当前线程更早请求资源根据公平性当前线程必须获取失败、继续排队只有当前节点没有前驱节点时才值得继续做 CAS 抢锁。由此可以得出关键结论公平锁每次都是从同步队列中的第一个节点获取到锁即严格按 FIFO 顺序放行非公平锁则不一定有可能刚释放锁的线程能立刻再次获取到锁因为它刚释放完紧接着就尝试 CAS此时同步队列里的老线程还没来得及被唤醒参与竞争从而造成饥饿风险。仓库 03.锁一些基础概念.md 第 1.3 节对两者的取舍给出了更系统的总结公平锁保证请求资源的绝对时间顺序但要频繁切换上下文非公平锁能减少上下文切换、保证更大的系统吞吐量。这正是 ReentrantLock 默认选择非公平锁的原因——用少量的插队换取整体吞吐。5.4 公平 / 非公平锁实测案例原文档的测试案例10 个线程Thread-5 ~ Thread-14共享同一个Service中的锁先各自打印抢到了锁再尝试lock.lock()并打印已经被锁定。private void test3() { Service service new Service(); ThreadClass tcArray[] new ThreadClass[10]; for (int i 0; i 10; i) { tcArray[i] new ThreadClass(service); tcArray[i].start(); } } public class Service { ReentrantLock lock new ReentrantLock(true); // true 公平锁 Service() { } void getThreadName() { System.out.println(Thread.currentThread().getName() 已经被锁定); } } public class ThreadClass extends Thread { private Service service; ThreadClass(Service service) { this.service service; } public void run() { System.out.println(Thread.currentThread().getName() 抢到了锁); service.lock.lock(); service.getThreadName(); service.lock.unlock(); } }公平锁new ReentrantLock(true)实测输出节选Thread-5 抢到了锁 Thread-5 已经被锁定 Thread-6 抢到了锁 Thread-6 已经被锁定 Thread-7 抢到了锁 Thread-8 抢到了锁 Thread-7 已经被锁定 Thread-8 已经被锁定 Thread-9 抢到了锁 Thread-9 已经被锁定 Thread-10 抢到了锁 Thread-10 已经被锁定 Thread-11 抢到了锁 Thread-11 已经被锁定 Thread-12 抢到了锁 Thread-12 已经被锁定 Thread-14 抢到了锁 Thread-14 已经被锁定 Thread-13 抢到了锁 Thread-13 已经被锁定非公平锁new ReentrantLock(false)实测输出节选Thread-5 抢到了锁 Thread-6 抢到了锁 Thread-5 已经被锁定 Thread-7 抢到了锁 Thread-7 已经被锁定 Thread-5 已经被锁定 - 实际为 Thread-5/6/7 交错 Thread-6 已经被锁定 ...对比可以得出结论公平锁下哪个线程先运行、先发出申请就能按顺序得到锁输出中 抢到了锁 与 已经被锁定 基本保持先到先得的次序非公平锁下新线程可能抢占已经在排队的线程的锁多个线程抢到了锁的交错出现谁先抢到完全看 CAS 运气。这也直接呼应了hasQueuedPredecessors的源码语义——公平锁把排队顺序硬编码进了获取逻辑非公平锁则跳过这一步直接竞争。06. 重入性ReentrantLock 重入锁state 计数与完全释放6.1 什么是重入锁ReentrantLock 是Lock接口的实现类也是实际编程中使用频率很高的一个锁支持重入性Reentrant——即当前线程获取该锁之后再次获取不会被阻塞能够对共享资源重复加锁。Java 关键字synchronized也隐式支持重入通过获取自增、释放自减的方式实现详见 04.Synchronize细说.md 第 4.1 节对monitorenter/monitorexit计数器的讲解——每个对象维护一个被锁次数计数器获取加 1释放减 1归零才真正释放与此同时ReentrantLock 还支持公平锁和非公平锁两种方式。要完全弄懂 ReentrantLock核心就是理解它的同步语义共两条重入性的实现原理公平锁与非公平锁已在上节讲完。6.2 重入要解决的两个问题要支持重入性必须解决两个问题线程获取锁时如果已经持有锁的线程就是当前线程则直接再次获取成功锁被获取 n 次就必须被释放同样的 n 次锁才算完全释放成功。同步组件通过重写 AQS 的 protected 方法tryAcquire/tryRelease来表达这两条语义。6.3 获取nonfairTryAcquire 的计数逻辑以非公平锁为例判断当前线程能否获得锁的核心方法是nonfairTryAcquirefinal boolean nonfairTryAcquire(int acquires) { final Thread current Thread.currentThread(); int c getState(); // 1. 如果该锁未被任何线程占有该锁能被当前线程获取 if (c 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } // 2. 若被占有检查占有线程是否是当前线程 else if (current getExclusiveOwnerThread()) { // 3. 再次获取计数加一 int nextc c acquires; if (nextc 0) // overflow throw new Error(Maximum lock count exceeded); setState(nextc); return true; } return false; }逻辑拆解第一步锁空闲state 0时通过CAS把同步状态从 0 改为acquires通常为 1成功则独占setExclusiveOwnerThread记录持有线程第二步锁已被占用时检查占用线程是否为当前线程——这是重入的关键。如果是把同步状态state加 1 并返回true表示当前线程再次获取成功若状态被加爆nextc 0即 int 溢出抛出Error(Maximum lock count exceeded)。可见每次重入都会对同步状态做加一操作state的值语义就是当前持有线程的重入次数。6.4 释放tryRelease 的递减与归零判断释放的处理思路依然以非公平锁为例核心方法是tryReleaseprotected final boolean tryRelease(int releases) { // 1. 同步状态减 1 int c getState() - releases; if (Thread.currentThread() ! getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free false; if (c 0) { // 2. 只有当同步状态为 0 时锁成功被释放返回 true free true; setExclusiveOwnerThread(null); } // 3. 锁未被完全释放返回 false setState(c); return free; }要点释放前先校验Thread.currentThread() getExclusiveOwnerThread()否则抛IllegalMonitorStateException——防止非持锁线程替别人解锁同步状态减releases通常为 1只有减到 0 时才把独占线程置空返回true表示完全释放如果锁被获取 n 次、只释放了 n-1 次state仍大于 0返回false说明锁尚未完全释放其他线程依然无法获取。到这里就理清了 ReentrantLock 重入性的完整实现获取时计数加一释放时计数减一计数归零才真正交出锁。外层sync.release(1)对应unlock()正是先走tryRelease判定再据此决定是否唤醒后继等待线程。07. 锁绑定多个条件Condition 与生产者-消费者实战7.1 Condition 解决了什么问题条件变量很大程度是为了解决Object.wait/notify/notifyAll难以使用的问题。在synchronized中所有线程都挤在同一个 object 的条件队列上等待区分不同等待原因非常麻烦而ReentrantLock中每个Condition都独立维护一个条件队列一把锁可以绑定任意多个Condition。对应关系如下Condition 方法Object 方法说明await()wait()释放锁并挂起等待被唤醒signal()notify()唤醒条件队列中的一个等待线程signalAll()notifyAll()唤醒条件队列中的全部等待线程Condition改名的目的正是为了避免与Object的wait/notify/notifyAll在语义和使用上混淆。每一个Condition都与Lock绑定因此继承了 Lock 的公平性特性公平锁下线程按 FIFO 顺序从Condition.await中释放非公平锁下后续锁竞争不保证 FIFO。7.2 生产者-消费者中的多条件实践原文档给出了一个基于Condition的有界队列生产/消费两个条件变量完整代码见 07.ReentrantLock原理.md核心思路如下public class QueueT { private final T[] items; private final Lock lock new ReentrantLock(); private Condition notFull lock.newCondition(); // 不满可继续 put private Condition notEmpty lock.newCondition(); // 不空可继续 take private int head, tail, count; public void put(T t) throws InterruptedException { lock.lock(); try { while (count items.length) { notFull.await(); // 数组满生产者挂起等待 } items[tail] t; if (tail items.length) tail 0; count; notEmpty.signal(); // 唤醒一个等待的消费者 } finally { lock.unlock(); } } public T take() throws InterruptedException { lock.lock(); try { while (count 0) { notEmpty.await(); // 数组空消费者挂起等待 } T o items[head]; items[head] null; // 置空便于 GC if (head items.length) head 0; --count; notFull.signal(); // 唤醒一个等待的生产者 return o; } finally { lock.unlock(); } } }与单一条件队列方案相比notFull/notEmpty两个条件变量的价值在于满时只挂起生产者、空时只挂起消费者互相唤醒互不干扰——这是synchronized单条件队列很难优雅做到的。7.3 await / signal 的底层机制假设线程 A、B 并发往队列插入数据当数组存满时线程 A 获取到锁并执行await()。ConditionObject.await的核心流程原文档源码摘录addConditionWaiter()把线程 A 封装成Node.CONDITION状态的节点加入条件等待队列若队尾是取消状态节点先清理fullyRelease(node)线程 A释放锁实质是把 AQS 的state置 0并唤醒 AQS 同步队列中的后继线程如 BB 被唤醒后重新竞争锁若线程 A 已不在同步队列中则通过LockSupport.park(this)挂起自己等待被唤醒被唤醒后走acquireQueued(node, savedState)重新竞争锁失败继续挂起成功则从await()位置恢复执行并恢复之前保存的重入计数savedState。相应地线程 B 持锁执行take()并调用signal()时源码见 07.ReentrantLock原理.md取出条件队列中第一个非 CANCELLED 节点即线程 AsignalAll则唤醒所有非 CANCELLED 节点遇到取消节点顺手清理通过transferForSignal用CAS 把节点waitStatus从 CONDITION 改为 0再调用enq(node)把线程 A插入 AQS 同步队列尾部视前驱节点状态决定是否LockSupport.unpark(node.thread)唤醒线程 A让 A 与其他线程一起公平或非公平竞争锁。简而言之await 是入条件队列 → 释放锁 → park 挂起signal 是出条件队列 → 入同步队列 → 可能 unpark 唤醒。理解这套流转就能把 ReentrantLock 的锁 条件体系与 AQS 的同步队列、Condition 的条件队列两套队列彻底打通。08. ReentrantLock 与 synchronized 全面对比仓库 08.两种不同锁区别.md 给出了两者非常系统的对比结合本篇内容汇总如下8.1 相似点两者都是加锁方式同步、阻塞式同步当某个线程获得对象锁并进入同步块后其他访问该同步块的线程必须阻塞在同步块外等待。而线程阻塞与唤醒的代价较高操作系统需要在用户态与内核态之间来回切换这也是 JVM 后来引入锁优化改善 synchronized 的原因。8.2 API 层面synchronized是Java 语言关键字属于原生语法层面的互斥由JVM 实现同步代码块编译后生成monitorenter/monitorexit字节码指令同步方法则带有ACC_SYNCHRONIZED标志详见 04.Synchronize细说.mdReentrantLock是JDK 1.5 之后提供的 API 层面的互斥锁需要lock()与unlock()方法配合try/finally语句块手动完成synchronized既可修饰方法也可修饰代码块Lock只适用于代码块锁但使用更灵活。8.3 等待可中断synchronized若持有锁的线程不释放等待线程只能一直等待不能被中断ReentrantLock等待线程可以lockInterruptibly()等方式响应中断等待很久后可放弃等待转做其他事对执行时间很长的同步块非常有帮助。8.4 公平性synchronized的锁是非公平锁ReentrantLock默认也是非公平锁但可通过带 boolean 的构造函数要求公平锁true公平、false非公平。公平锁按请求顺序依次获得锁但有成本非公平锁允许讨价还价抢占。8.5 锁绑定多个条件ReentrantLock可同时绑定多个Condition多次调用newCondition()即可synchronized中锁对象的wait()/notify()/notifyAll()只能实现一个隐含条件要与多个条件关联时就不得不额外添加锁。8.6 异常与锁的释放synchronized发生异常时自动释放线程占有的锁因此不会因异常导致死锁Lock发生异常时若没有主动unlock()很可能造成死锁所以必须在finally中释放锁。8.7 其他能力通过Lock可以知道有没有成功获取锁tryLock()返回 boolean而synchronized无法办到Lock还具备尝试非阻塞获取锁、可中断获取锁、超时获取锁指定时间范围内获取超时返回等特性Lock可以提高多个线程进行读操作的效率配合读写锁体系使用。8.8 性能结论竞争不激烈时两者性能差不多竞争非常激烈大量线程同时竞争时ReentrantLock 的性能优于 synchronizedJDK 1.5 中 synchronized 是重量级操作挂起/恢复线程需转入内核态性能低效JDK 1.6 加入自适应自旋、锁消除、锁粗化、轻量级锁、偏向锁等优化后synchronized 性能已不逊于 Lock。因此最终选型建议是在 synchronized 能满足需求的情况下优先考虑 synchronized只有当确实需要可中断、可超时、多条件、公平锁等 synchronized 不具备的特性或处于高争用场景需要更高吞吐时才引入 ReentrantLock。09. 小结ReentrantLock 知识清单主题核心要点本质java.util.concurrent下的互斥锁实现Lock接口基于 AQS三大高级特性等待可中断、公平锁、锁绑定多个条件使用纪律lock()在 try 外、unlock()在 finally 中、成对出现重入性获取state1释放state-1归零才完全释放setExclusiveOwnerThread记录持有者公平锁构造传truetryAcquire中hasQueuedPredecessors()保证 FIFO每次从同步队列第一个节点拿锁非公平锁默认实现跳过排队检查直接 CAS 抢占吞吐更高但可能造成饥饿Condition对应Object.wait/notify/notifyAll多条件队列实现精细的生产者-消费者协作与 synchronizedAPI 层互斥 vs JVM 关键字异常自动释放 vs 必须 finally 释放可中断/可超时 vs 不可中断高争用下 ReentrantLock 更有优势本文是 YCBlogs 仓库 java/07.Java并发 系列中关于显式锁的完整一篇可与同目录下 03.锁一些基础概念.md锁的分类全景、07.ReentrantLock原理.md完整源码与 Condition 机制、08.两种不同锁区别.md与 synchronized 系统对比、04.Synchronize细说.mdsynchronized 用法与原理配合阅读即可形成从关键字锁到显式锁、从使用到 AQS 源码的完整知识闭环。赞分享教程技术博客文档【免费下载链接】YCBlogs技术博客笔记大汇总包括Java基础线程并发数据结构Android技术博客等等常用设计模式常见的算法网络协议知识点部分flutter笔记还包括平时开发中遇到的bug汇总当然也在工作之余收集了大量的面试题长期更新维护并且修正持续完善……开源的文件是markdown格式的转载请注明出处谢谢项目地址https://gitcode.com/gh_mirrors/yc/YCBlogs点击查看免费下载相关推荐JCSprout 源码解读ReentrantLock 实现原理AQS 公平锁与非公平锁全解析JCSprout 源码解读ReentrantLock 实现原理AQS 公平锁与非公平锁全解析 ReentrantLock 是 JDK 提供的显式可重入锁文档知识库后端教程YCBlogs Java 并发专题ReentrantLock 原理深度解析——从 AQS 委托、重入语义到 Condition 条件队列YCBlogs Java 并发专题ReentrantLock 原理深度解析——从 AQS 委托、重入语义到 Condition 条件队列 本文是 YCBlog教程技术博客文档JCSprout 源码级详解ReentrantLock 实现原理与 AQS 公平锁/非公平锁机制JCSprout 源码级详解ReentrantLock 实现原理与 AQS 公平锁/非公平锁机制 ReentrantLock 是 Java 并发编程中除 sy文档知识库后端教程上一篇碧蓝航线终极自动化指南Alas脚本实现7x24小时全自动游戏管理下一篇DLSS Swapper3分钟学会游戏DLSS文件管理免费解锁显卡隐藏性能创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进