CAS1、什么是CASCAS(Compare And Swap比较与交换)非堵塞同步实现原理是cpu硬件层面的一种指令。通过硬件实现比较与交换的原子性。CAS指令包括三个参数内存值V(要修改的变量)、预期值E、新值N。执行时先读取当前值如果当前值等于预期值则将内存修改为新值否则不做修改。无论修改与否都会返回旧的内存值。整个过程是原子的由 CPU 指令保证。2、Java 中的 CAS 实现sun.misc.Unsafe 类提供了底层的 CAS 方法如publicfinalnativebooleancompareAndSwapInt(Objectobj,longoffset,intexpect,intupdate);publicfinalnativebooleancompareAndSwapObject(Objectobj,longoffset,Objectexpect,Objectupdate);参数解释obj:对象实例offset内存偏移量、expect字段期望值、update字段新值//获取unsafe对象FieldtheUnsafeUnsafe.class.getDeclaredField(theUnsafe);theUnsafe.setAccessible(true);Unsafeunsafe(Unsafe)theUnsafe.get(null);//获取字段内存偏移量longxunsafe.objectFieldOffset(Entity.class.getDeclaredField(x));EntityentitynewEntity();booleanb;bunsafe.compareAndSwapObject(entity,x,null,abc);System.out.println(1b-b);// truebunsafe.compareAndSwapObject(entity,x,null,abc);System.out.println(2b-b);// falsenative 方法直接调用 CPU 的 CAS 指令Java 并发包java.util.concurrent.atomic中的原子类如 AtomicInteger正是基于 Unsafe 的 CAS 实现。3、CAS 的优点无锁不会引起线程阻塞与上下文切换在高并发低竞争场景下优于synchronized。可以看作是乐观锁的一种实现方式。轻量级不需要操作系统调度减小开销乐观并发冲突少时失败重试适合读多写少场景4、CAS 的缺点1自旋开销CAS失败时通常会在循环中重试自旋如果长时间不成功会给cpu带来大量开销。所以CAS适用于短时间竞争2只能保证单个变量的原子操作无法保证多个变量同时操作3ABA问题当一个内存值变量从a改成b又从b改回a则CAS认为没有发生任何变化但中间可能发生其他操作例如Thread 1、2、3需要修改变量 a过程1、thread 1 获取内存值a1此时线程被挂起2、thread 2 获取内存值a1修改为 a23、thread 3 获取内存值a2修改为 a14、thread 1 恢复后执行 CAS发现当前值仍然是 1(与期望值相同)认为a没有被其他线程修改过继续执行后续操作。5、ABA问题的解决方案通过版本号/标记位是否改变来检测内存值是否发生变化。版本号基于数据版本实现数据同步的机制每次修改一次数据版本就会进行累加如数据库乐观锁。Java 提供了 AtomicStampedReference 和 AtomicMarkableReference 原子引用类来解决 ABA 问题。AtomicStampedReference 内部维护 [reference, stamp] 对每次修改时同时比较引用和版本号stamp修改后的版本号往上递增通过版本号检测 “值被改过又改回” 的中间变化。reference 并不是直接的内存地址数值而是 对象引用即指向堆中对象的“指针”AtomicStampedReference 比较的是引用是否指向同一个对象与 stamp版本戳 是否相等。AtomicMarkableReference类似但版本号只有 true/false适用于只需标记是否修改过的场景。6、CAS应用场景原子类AtomicInteger、AtomicLong、AtomicReference 等。AQSAbstractQueuedSynchronizer用于同步状态 state 的更新和 CLH 队列的入队操作。并发容器如 ConcurrentHashMap 在 Java 8 中使用 CAS 进行初始化、节点替换等。自旋锁简单的自旋锁可通过 CAS 实现。7、Atomic原子操作类实现方式原子性通过 Unsafe 类直接调用 CPU 原子指令实现“比较并交换”的原子性volatile保证CAS 操作后其他线程能立即看到最新值保证变量的内存可见性无锁多个线程同时操作时失败的线程会自旋重试而不是立即挂起Atomic分类基本类型对 int、long、boolean 进行原子更新如 AtomicInteger、AtomicLong、AtomicBoolean数组类型对数组元素进行原子更新如AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray引用类型对对象引用进行原子更新如AtomicReference、AtomicStampedReference、AtomicMarkableReference后两者用于解决 ABA 问题。字段更新器 基于反射对普通 volatile 字段进行原子更新减少对象包装开销如AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater累加器JDK 8 用于高并发统计场景性能优于 AtomicLong内部分段累加最后汇总如 LongAdder、DoubleAdder、LongAccumulator、DoubleAccumulatorvolatilevolatile 是 Java 的一种轻量级同步机制用于修饰变量。它保证可见性和有序性但不保证原子性。使用场景1状态标志开关状态的变化对其他线程立即可见且无需加锁。2双重检查锁DCL中的单例volatile 防止 instance new Singleton() 内部指令重排分配内存、初始化、赋值导致其他线程读到未完全初始化的对象。3读多写少、无需复合操作的配置参数。总结volatile 是一个轻量、高效的同步工具适用于一个线程写、多个线程读的简单状态标志或需要禁止重排序的场合。对于复合操作仍应使用锁或原子类。正确使用 volatile 可以避免过度同步提升性能。AbstractQueuedSynchronizer (AQS)1、什么是AQSAbstractQueuedSynchronizer 是 java.util.concurrent 包的基石ReentrantLock、Semaphore、CountDownLatch、ReentrantReadWriteLock 等同步器都基于它实现。AQS 提供了一个框架用于实现依赖 状态state 和 FIFO 等待队列 的阻塞锁和同步器。2、AQS核心结构1. 同步状态 state- private volatile int state;state表示资源的可用状态ReentrantLock0 表示未锁定0 表示锁定可重入计数。Semaphore表示剩余许可数。CountDownLatch表示需要倒计数的数量。2. CLH 队列FIFO 同步队列作用维护获取锁失败时入队的线程CLH 队列一种基于双向链表数据结构的队列是FIFO先进先出线程等待队列当前线程如果获取同步状态失败时AQS则会将当前线程已经等待状态等信息构造成一个节点Node并将其加入到CLH同步队列同时阻塞当前线程。当同步状态释放时会把首节点唤醒公平锁使其再次尝试获取同步状态。通过signal或signalAll将条件队列中的节点转移到同步队列。由条件队列转化为同步队列Node 节点AQS 内部维护一个 双向链表节点 Node 包含thread等待的线程prev、next前驱和后继指针waitStatus等待状态CANCELLED1, SIGNAL-1, CONDITION-2, PROPAGATE-3nextWaiter用于条件队列的链接队列结构head-Node1-Node2-...-tailhead 是当前持有锁的线程节点tail 指向队尾。入队操作通过 CAS 设置 tail 实现无锁并发。出队时head 指向下一个线程节点。waitStatus状态值为0初始化状态表示当前节点在sync队列中等待着获取锁。CANCELLED值为1表示当前的线程被取消SIGNAL值为-1表示当前节点的后继节点包含的线程需要运行也就是unparkCONDITION值为-2表示当前节点在等待condition也就是在condition队列中PROPAGATE值为-3表示当前场景下后续的acquireShared能够得以执行3. 条件队列使用单向列表保存使用nextWaiter连接调用await方法阻塞线程当前线程存在于同步队列的头结点从同步队列转化到条件队列。AQS 内部实现了 Condition 接口。每个 Condition 对象拥有一个单向队列链表 Node用于存放调用 await() 而阻塞的线程。当其他线程调用 signal() 时会将条件队列中的节点转移到同步队列等待获取锁。3、ReentrantLockReentrantLock 是基于 AQS 实现的可重入独占锁。支持公平/非公平选择、可响应中断、超时获取、多条件变量。公平锁与非公平锁ReentrantLock 默认非公平锁但构造时可指定 fair true 创建公平锁。ReentrantLock 内部维护一个 Sync 抽象类继承自 AbstractQueuedSynchronizer。实现非公平锁NonfairSync默认与公平锁FairSyncAQS中state字段ReentrantLock 中使用AQS中state字段表示重入次数。state 0锁空闲。state 0锁被某个线程持有数值表示重入次数同一线程多次 lock 会递增公平锁与非公平锁的区别非公平锁新线程在 state0 时立即尝试 CAS 抢锁不管同步队列中是否有等待线程。公平锁新线程在 state0 时会先检查队列中是否有前驱等待线程若队列中已有等待线程则入队排队不参与抢占。lock() 非公平锁加锁流程1、快速尝试加锁当前线程调用lock立即通过CAS修改 AQS 的 state 属性值 尝试将state 从 0 改为 1。成功当前线程获取锁记录锁的持有者为当前线程exclusiveOwnerThread currentThread重入次数为1失败执行AQS的acquire方法进入常规获取锁流程2、常规获取锁过程再次尝试获取锁使用tryAcquire(1)方法获取当前 state(1)若 state 0锁空闲再次 CAS 尝试获取锁成功则设置独占线程为当前线程返回 true。(2)若 state 0 且当前线程就是锁的持有者执行可重入 state 1返回 true其他情况返回false抢锁失败。然加入同步队列addWaiter()3、加入同步队列addWaiter当 tryAcquire 返回 false 时AQS 会为当前线程创建一个 Node 节点模式为独占并通过 CAS 将其插入到 CLH 队列的尾部。如果队列未初始化会先初始化一个空的头节点enq 方法自旋。4、自旋与挂起acquireQueued节点入队后检查自己的前驱节点是否为头节点head如果是尝试获取锁资源若获取成功将当前节点设为新的头节点原头节点出队help GC线程返回加锁成功。获取失败或者不是头节点将前驱节点的 waitStatus 设为-1表示后继需要被唤醒。然后挂起当前线程LockSupport.park。总结快速 CAS 尝试 → 可重入检查 → 失败后入队 → 自旋阻塞 → 被唤醒后竞争锁。非公平锁允许在 state0 时进行插队而公平锁严格遵循队列顺序。整个流程完全基于 AQS 的 state CLH 队列 CAS LockSupport 实现保证了高并发下的正确性与性能。公平锁加锁流程FairSync公平锁加锁不会进行快速尝试加锁使用常规获取锁过程并且在state 0时公平锁会先判断同步队列中是否存在等待时间更长的线程即队列非空且当前线程不是头节点的后继。如果存在加锁失败当前线程必须排队不能抢占锁。只有队列为空或当前线程本身就是队首等待线程时才允许 CAS 获取锁。锁释放过程计算新的 state 值c state - 1因为 releases1。检查当前线程是否为锁的持有者若不是则抛出 异常。如果 c 0说明锁已经完全释放所有重入锁已退出将 exclusiveOwnerThread 设为 null表示锁不再被任何线程持有。 通过 setState© 更新 AQS 的 state 字段注意这里不需要 CAS因为锁由当前线程独占没有并发写锁已完全空闲release 唤醒后继线程获取当前队列的头节点 head如果 head 不为空且 waitStatus -1从队列中找到头节点之后第一个未被取消的节点调用 LockSupport.unpark(thread) 唤醒该节点对应的线程。被唤醒线程的后续动作被唤醒的线程原先因为 park() 而阻塞一旦被 unpark继续执行自旋循环检查自己的前驱是否为头节点。如果是则尝试获取锁。成功:将自己设为新头节点加锁成功。失败:极端情况如又被非公平锁抢先可能再次自旋或阻塞。释放锁总结减少 AQS 的 state 值重入计数减 1。如果 state 变为 0则清空独占线程记录并通知 AQS 锁已空闲。AQS 检查同步队列头节点如果头节点 waitStatus SIGNAL则唤醒其后继节点。唤醒操作通过 LockSupport.unpark 实现被唤醒线程将重新竞争锁ConditionCondition 是 JUC 中提供的线程等待/通知机制必须与 ReentrantLock 配合使用。其核心实现是 AQS 的内部类 ConditionObject它利用了 AQS 的同步队列和条件队列实现了高效的等待/通知Condition整体结构每个 ConditionObject 维护一个条件队列单向链表用来存放调用了 await() 而阻塞的线程节点。同时AQS 本身还有一个同步队列CLH 双向队列用来存放竞争锁而阻塞的线程。一个 Lock 可以创建多个 Condition 实例如 notFull、notEmpty。每个 Condition 拥有自己独立的条件队列。节点在不同队列间转移await 时从同步队列进入条件队列signal 时从条件队列回到同步队列。node节点结构在同步队列中nextWaiter 可以用来表示节点是独占模式还是共享模式Node.EXCLUSIVE / Node.SHARED。在条件队列中nextWaiter 指向下一个条件等待节点当节点在条件队列中时其 waitStatus 被设置为 CONDITION -2。核心方法void await()必须在持有锁时调用调用该方发释放当前线程锁持有锁并创建nodo节点加入等待队列。boolean await(long time, TimeUnit unit)必须在持有锁时调用调用该方发释放当前线程锁持有锁并创建nodo节点加入等待队列等待time时间超时后移入同步队列获取锁后执行返回false表示超时唤醒。等待time时间超时内移入同步队列获取锁后执行返回true表示是满足条件唤醒。返回值只与超时是否发生有关与最终是否获取锁无关。await 的返回值是为了让调用者知道条件是否在期望的时间内变成真如果超时后条件才变成真那对于需要实时响应的业务来说已经太晚了例如等待某个资源可用但超时后决定放弃操作。即使最终获得了锁业务逻辑也应该根据返回值判断是否要继续等待条件而不是简单依赖于锁的获取。void signal()必须在持有锁时调用将在此条件队列上的一个线程移入同步队列并不立即释放锁只有执行到reentrantLock.unlock后才会释放锁。void signalAll()与 signal 类似但它会遍历整个条件队列将每个节点都转移到同步队列并不立即释放锁只有执行到reentrantLock.unlock后才会释放锁。转移后的节点如何获取锁公平锁转移后的节点获取锁的顺序等于它被转移到同步队列的顺序也等于它在条件队列中等待的顺序因为条件队列也是 FIFO。不会有新来的线程插在它前面。非公平锁转移后的节点可能被后续新来的线程插队入队前直接CAS抢锁获取锁的时间不确定但最终总能获取到除非锁被永久占用。这就是非公平锁的“吞吐量优先”特性。还有可能被其他正在lock的线程直接CAS抢锁。因此在非公平锁下从条件队列转移出来的线程很可能需要等待更长时间甚至比后来才请求锁的新线程更晚获得锁
JUC-AQS与ReentrantLock
CAS1、什么是CASCAS(Compare And Swap比较与交换)非堵塞同步实现原理是cpu硬件层面的一种指令。通过硬件实现比较与交换的原子性。CAS指令包括三个参数内存值V(要修改的变量)、预期值E、新值N。执行时先读取当前值如果当前值等于预期值则将内存修改为新值否则不做修改。无论修改与否都会返回旧的内存值。整个过程是原子的由 CPU 指令保证。2、Java 中的 CAS 实现sun.misc.Unsafe 类提供了底层的 CAS 方法如publicfinalnativebooleancompareAndSwapInt(Objectobj,longoffset,intexpect,intupdate);publicfinalnativebooleancompareAndSwapObject(Objectobj,longoffset,Objectexpect,Objectupdate);参数解释obj:对象实例offset内存偏移量、expect字段期望值、update字段新值//获取unsafe对象FieldtheUnsafeUnsafe.class.getDeclaredField(theUnsafe);theUnsafe.setAccessible(true);Unsafeunsafe(Unsafe)theUnsafe.get(null);//获取字段内存偏移量longxunsafe.objectFieldOffset(Entity.class.getDeclaredField(x));EntityentitynewEntity();booleanb;bunsafe.compareAndSwapObject(entity,x,null,abc);System.out.println(1b-b);// truebunsafe.compareAndSwapObject(entity,x,null,abc);System.out.println(2b-b);// falsenative 方法直接调用 CPU 的 CAS 指令Java 并发包java.util.concurrent.atomic中的原子类如 AtomicInteger正是基于 Unsafe 的 CAS 实现。3、CAS 的优点无锁不会引起线程阻塞与上下文切换在高并发低竞争场景下优于synchronized。可以看作是乐观锁的一种实现方式。轻量级不需要操作系统调度减小开销乐观并发冲突少时失败重试适合读多写少场景4、CAS 的缺点1自旋开销CAS失败时通常会在循环中重试自旋如果长时间不成功会给cpu带来大量开销。所以CAS适用于短时间竞争2只能保证单个变量的原子操作无法保证多个变量同时操作3ABA问题当一个内存值变量从a改成b又从b改回a则CAS认为没有发生任何变化但中间可能发生其他操作例如Thread 1、2、3需要修改变量 a过程1、thread 1 获取内存值a1此时线程被挂起2、thread 2 获取内存值a1修改为 a23、thread 3 获取内存值a2修改为 a14、thread 1 恢复后执行 CAS发现当前值仍然是 1(与期望值相同)认为a没有被其他线程修改过继续执行后续操作。5、ABA问题的解决方案通过版本号/标记位是否改变来检测内存值是否发生变化。版本号基于数据版本实现数据同步的机制每次修改一次数据版本就会进行累加如数据库乐观锁。Java 提供了 AtomicStampedReference 和 AtomicMarkableReference 原子引用类来解决 ABA 问题。AtomicStampedReference 内部维护 [reference, stamp] 对每次修改时同时比较引用和版本号stamp修改后的版本号往上递增通过版本号检测 “值被改过又改回” 的中间变化。reference 并不是直接的内存地址数值而是 对象引用即指向堆中对象的“指针”AtomicStampedReference 比较的是引用是否指向同一个对象与 stamp版本戳 是否相等。AtomicMarkableReference类似但版本号只有 true/false适用于只需标记是否修改过的场景。6、CAS应用场景原子类AtomicInteger、AtomicLong、AtomicReference 等。AQSAbstractQueuedSynchronizer用于同步状态 state 的更新和 CLH 队列的入队操作。并发容器如 ConcurrentHashMap 在 Java 8 中使用 CAS 进行初始化、节点替换等。自旋锁简单的自旋锁可通过 CAS 实现。7、Atomic原子操作类实现方式原子性通过 Unsafe 类直接调用 CPU 原子指令实现“比较并交换”的原子性volatile保证CAS 操作后其他线程能立即看到最新值保证变量的内存可见性无锁多个线程同时操作时失败的线程会自旋重试而不是立即挂起Atomic分类基本类型对 int、long、boolean 进行原子更新如 AtomicInteger、AtomicLong、AtomicBoolean数组类型对数组元素进行原子更新如AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray引用类型对对象引用进行原子更新如AtomicReference、AtomicStampedReference、AtomicMarkableReference后两者用于解决 ABA 问题。字段更新器 基于反射对普通 volatile 字段进行原子更新减少对象包装开销如AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater累加器JDK 8 用于高并发统计场景性能优于 AtomicLong内部分段累加最后汇总如 LongAdder、DoubleAdder、LongAccumulator、DoubleAccumulatorvolatilevolatile 是 Java 的一种轻量级同步机制用于修饰变量。它保证可见性和有序性但不保证原子性。使用场景1状态标志开关状态的变化对其他线程立即可见且无需加锁。2双重检查锁DCL中的单例volatile 防止 instance new Singleton() 内部指令重排分配内存、初始化、赋值导致其他线程读到未完全初始化的对象。3读多写少、无需复合操作的配置参数。总结volatile 是一个轻量、高效的同步工具适用于一个线程写、多个线程读的简单状态标志或需要禁止重排序的场合。对于复合操作仍应使用锁或原子类。正确使用 volatile 可以避免过度同步提升性能。AbstractQueuedSynchronizer (AQS)1、什么是AQSAbstractQueuedSynchronizer 是 java.util.concurrent 包的基石ReentrantLock、Semaphore、CountDownLatch、ReentrantReadWriteLock 等同步器都基于它实现。AQS 提供了一个框架用于实现依赖 状态state 和 FIFO 等待队列 的阻塞锁和同步器。2、AQS核心结构1. 同步状态 state- private volatile int state;state表示资源的可用状态ReentrantLock0 表示未锁定0 表示锁定可重入计数。Semaphore表示剩余许可数。CountDownLatch表示需要倒计数的数量。2. CLH 队列FIFO 同步队列作用维护获取锁失败时入队的线程CLH 队列一种基于双向链表数据结构的队列是FIFO先进先出线程等待队列当前线程如果获取同步状态失败时AQS则会将当前线程已经等待状态等信息构造成一个节点Node并将其加入到CLH同步队列同时阻塞当前线程。当同步状态释放时会把首节点唤醒公平锁使其再次尝试获取同步状态。通过signal或signalAll将条件队列中的节点转移到同步队列。由条件队列转化为同步队列Node 节点AQS 内部维护一个 双向链表节点 Node 包含thread等待的线程prev、next前驱和后继指针waitStatus等待状态CANCELLED1, SIGNAL-1, CONDITION-2, PROPAGATE-3nextWaiter用于条件队列的链接队列结构head-Node1-Node2-...-tailhead 是当前持有锁的线程节点tail 指向队尾。入队操作通过 CAS 设置 tail 实现无锁并发。出队时head 指向下一个线程节点。waitStatus状态值为0初始化状态表示当前节点在sync队列中等待着获取锁。CANCELLED值为1表示当前的线程被取消SIGNAL值为-1表示当前节点的后继节点包含的线程需要运行也就是unparkCONDITION值为-2表示当前节点在等待condition也就是在condition队列中PROPAGATE值为-3表示当前场景下后续的acquireShared能够得以执行3. 条件队列使用单向列表保存使用nextWaiter连接调用await方法阻塞线程当前线程存在于同步队列的头结点从同步队列转化到条件队列。AQS 内部实现了 Condition 接口。每个 Condition 对象拥有一个单向队列链表 Node用于存放调用 await() 而阻塞的线程。当其他线程调用 signal() 时会将条件队列中的节点转移到同步队列等待获取锁。3、ReentrantLockReentrantLock 是基于 AQS 实现的可重入独占锁。支持公平/非公平选择、可响应中断、超时获取、多条件变量。公平锁与非公平锁ReentrantLock 默认非公平锁但构造时可指定 fair true 创建公平锁。ReentrantLock 内部维护一个 Sync 抽象类继承自 AbstractQueuedSynchronizer。实现非公平锁NonfairSync默认与公平锁FairSyncAQS中state字段ReentrantLock 中使用AQS中state字段表示重入次数。state 0锁空闲。state 0锁被某个线程持有数值表示重入次数同一线程多次 lock 会递增公平锁与非公平锁的区别非公平锁新线程在 state0 时立即尝试 CAS 抢锁不管同步队列中是否有等待线程。公平锁新线程在 state0 时会先检查队列中是否有前驱等待线程若队列中已有等待线程则入队排队不参与抢占。lock() 非公平锁加锁流程1、快速尝试加锁当前线程调用lock立即通过CAS修改 AQS 的 state 属性值 尝试将state 从 0 改为 1。成功当前线程获取锁记录锁的持有者为当前线程exclusiveOwnerThread currentThread重入次数为1失败执行AQS的acquire方法进入常规获取锁流程2、常规获取锁过程再次尝试获取锁使用tryAcquire(1)方法获取当前 state(1)若 state 0锁空闲再次 CAS 尝试获取锁成功则设置独占线程为当前线程返回 true。(2)若 state 0 且当前线程就是锁的持有者执行可重入 state 1返回 true其他情况返回false抢锁失败。然加入同步队列addWaiter()3、加入同步队列addWaiter当 tryAcquire 返回 false 时AQS 会为当前线程创建一个 Node 节点模式为独占并通过 CAS 将其插入到 CLH 队列的尾部。如果队列未初始化会先初始化一个空的头节点enq 方法自旋。4、自旋与挂起acquireQueued节点入队后检查自己的前驱节点是否为头节点head如果是尝试获取锁资源若获取成功将当前节点设为新的头节点原头节点出队help GC线程返回加锁成功。获取失败或者不是头节点将前驱节点的 waitStatus 设为-1表示后继需要被唤醒。然后挂起当前线程LockSupport.park。总结快速 CAS 尝试 → 可重入检查 → 失败后入队 → 自旋阻塞 → 被唤醒后竞争锁。非公平锁允许在 state0 时进行插队而公平锁严格遵循队列顺序。整个流程完全基于 AQS 的 state CLH 队列 CAS LockSupport 实现保证了高并发下的正确性与性能。公平锁加锁流程FairSync公平锁加锁不会进行快速尝试加锁使用常规获取锁过程并且在state 0时公平锁会先判断同步队列中是否存在等待时间更长的线程即队列非空且当前线程不是头节点的后继。如果存在加锁失败当前线程必须排队不能抢占锁。只有队列为空或当前线程本身就是队首等待线程时才允许 CAS 获取锁。锁释放过程计算新的 state 值c state - 1因为 releases1。检查当前线程是否为锁的持有者若不是则抛出 异常。如果 c 0说明锁已经完全释放所有重入锁已退出将 exclusiveOwnerThread 设为 null表示锁不再被任何线程持有。 通过 setState© 更新 AQS 的 state 字段注意这里不需要 CAS因为锁由当前线程独占没有并发写锁已完全空闲release 唤醒后继线程获取当前队列的头节点 head如果 head 不为空且 waitStatus -1从队列中找到头节点之后第一个未被取消的节点调用 LockSupport.unpark(thread) 唤醒该节点对应的线程。被唤醒线程的后续动作被唤醒的线程原先因为 park() 而阻塞一旦被 unpark继续执行自旋循环检查自己的前驱是否为头节点。如果是则尝试获取锁。成功:将自己设为新头节点加锁成功。失败:极端情况如又被非公平锁抢先可能再次自旋或阻塞。释放锁总结减少 AQS 的 state 值重入计数减 1。如果 state 变为 0则清空独占线程记录并通知 AQS 锁已空闲。AQS 检查同步队列头节点如果头节点 waitStatus SIGNAL则唤醒其后继节点。唤醒操作通过 LockSupport.unpark 实现被唤醒线程将重新竞争锁ConditionCondition 是 JUC 中提供的线程等待/通知机制必须与 ReentrantLock 配合使用。其核心实现是 AQS 的内部类 ConditionObject它利用了 AQS 的同步队列和条件队列实现了高效的等待/通知Condition整体结构每个 ConditionObject 维护一个条件队列单向链表用来存放调用了 await() 而阻塞的线程节点。同时AQS 本身还有一个同步队列CLH 双向队列用来存放竞争锁而阻塞的线程。一个 Lock 可以创建多个 Condition 实例如 notFull、notEmpty。每个 Condition 拥有自己独立的条件队列。节点在不同队列间转移await 时从同步队列进入条件队列signal 时从条件队列回到同步队列。node节点结构在同步队列中nextWaiter 可以用来表示节点是独占模式还是共享模式Node.EXCLUSIVE / Node.SHARED。在条件队列中nextWaiter 指向下一个条件等待节点当节点在条件队列中时其 waitStatus 被设置为 CONDITION -2。核心方法void await()必须在持有锁时调用调用该方发释放当前线程锁持有锁并创建nodo节点加入等待队列。boolean await(long time, TimeUnit unit)必须在持有锁时调用调用该方发释放当前线程锁持有锁并创建nodo节点加入等待队列等待time时间超时后移入同步队列获取锁后执行返回false表示超时唤醒。等待time时间超时内移入同步队列获取锁后执行返回true表示是满足条件唤醒。返回值只与超时是否发生有关与最终是否获取锁无关。await 的返回值是为了让调用者知道条件是否在期望的时间内变成真如果超时后条件才变成真那对于需要实时响应的业务来说已经太晚了例如等待某个资源可用但超时后决定放弃操作。即使最终获得了锁业务逻辑也应该根据返回值判断是否要继续等待条件而不是简单依赖于锁的获取。void signal()必须在持有锁时调用将在此条件队列上的一个线程移入同步队列并不立即释放锁只有执行到reentrantLock.unlock后才会释放锁。void signalAll()与 signal 类似但它会遍历整个条件队列将每个节点都转移到同步队列并不立即释放锁只有执行到reentrantLock.unlock后才会释放锁。转移后的节点如何获取锁公平锁转移后的节点获取锁的顺序等于它被转移到同步队列的顺序也等于它在条件队列中等待的顺序因为条件队列也是 FIFO。不会有新来的线程插在它前面。非公平锁转移后的节点可能被后续新来的线程插队入队前直接CAS抢锁获取锁的时间不确定但最终总能获取到除非锁被永久占用。这就是非公平锁的“吞吐量优先”特性。还有可能被其他正在lock的线程直接CAS抢锁。因此在非公平锁下从条件队列转移出来的线程很可能需要等待更长时间甚至比后来才请求锁的新线程更晚获得锁