java有哪些锁?讲讲AQS

1 个回答

  • 马九
    慢慢理解世界,慢慢更新自己。
    # 一、Java 有哪些“锁”(按语义/实现分类) ## 1) 语言级 * **`synchronized`**(对象监视器,JVM 指令 `monitorenter/monitorexit`) * 特性:可重入、不可中断获取、无超时、自动释放、异常即释放、内存语义强(解锁→加锁 *happens-before*)。 * HotSpot 锁状态:无锁 → 轻量级(自旋/CAS)→ 重量级(阻塞);**偏向锁 JDK15 起默认禁用,JDK18 起移除**。 * 适合:简单临界区、代码可读性优先。 ## 2) JUC 显式锁(`java.util.concurrent.locks`) * **`ReentrantLock`**:可重入;**可中断获取**、**可超时**、**可公平**、多 `Condition`。 * **`ReentrantReadWriteLock`**:读写分离(读共享、写独占),读多写少。 * **`StampedLock`**:写独占、读共享、**乐观读**(验证失败再退化);**不可重入**,不与条件队列配合,适合极端读多场景的短读路径。 > 以上三者的核心同步都建立在 **AQS** 上(后面详讲)。 ## 3) 同步器 / “信号量类”(也基于 AQS) * **`Semaphore`**(许可数控制并发度)、**`CountDownLatch`**(一次性栅栏)、**`CyclicBarrier`**(阶段对齐,非 AQS)、**`Phaser`**(多阶段对齐)、**`FutureTask`**/`SynchronousQueue` 等。 ## 4) 原子/无锁(不属于“锁”,但常与其对比) * **`volatile`**(可见性/有序性,不保证复合原子性) * **`Atomic*`/`LongAdder`/`VarHandle`**(CAS 乐观更新,冲突少时更高效) # 二、AQS 是什么 & 怎么工作(核心机制) **AQS(AbstractQueuedSynchronizer)** = 用一个 **`int state`** + **FIFO 等待队列(CLH 变体)** + **park/unpark** 封装出“独占/共享”两类同步语义的**模板**。 * **关键字段** * `volatile int state`:同步状态(如重入次数、剩余许可等),**CAS** 修改。 * **等待队列**:双向链表节点持有 `Thread`、模式(独占/共享)、等待状态。 * **两种模式** * **独占**:同一时刻最多 1 个持有者(`ReentrantLock`/写锁) * **共享**:同一时刻可多个持有者(读锁、`Semaphore` 许可 > 1) * **模板方法(子类实现)** * `tryAcquire(int)` / `tryRelease(int)`(独占) * `tryAcquireShared(int)` / `tryReleaseShared(int)`(共享) * AQS 负责排队、阻塞、唤醒、公平性、超时/可中断等通用流程。 * **获取流程(独占为例)** 1. 尝试 CAS 改 `state` → 成功则获得锁; 2. 失败则入队,按**公平/非公平**策略排队; 3. 排到头节点前驱可用时,`park()` 阻塞,等待被前驱释放后 `unpark()`; 4. 被唤醒再尝试获取,成功后成为新的 head。 * **释放流程** * 调用子类 `tryRelease` 成功 → 置 head 状态可传播 → 唤醒(`unpark`)后继节点(独占唤 1;共享可级联唤醒)。 * **可中断/超时** * AQS 内置 `acquireInterruptibly`、`tryAcquireNanos` 等,统一封装。 * **Condition 支持** * `ConditionObject`:条件队列与同步队列分离。`await()` 将当前线程从同步队列**移入条件队列并释放锁**;`signal()` 把条件队列的首节点转移回同步队列等待重新竞争锁。 * **可重入** * 子类(如 `ReentrantLock`)在 `tryAcquire` 中:同一线程再次获取则 `state++`;释放时 `state--` 直到 0 真释放。 > AQS 把“**算法(排队/阻塞/唤醒)**”与“**策略(如何判断可获取/释放)**”解耦,子类只实现 *try* 方法即可复用一整套可靠的并发控制流程。 # 三、基于 AQS 的典型实现如何映射到 `state` * **`ReentrantLock`**(独占):`state`=重入次数;`tryAcquire` 在 state==0 时 CAS 置为 1 并记录 owner,重入则 `state++`;`tryRelease` 递减到 0 时清 owner。 * **`ReentrantReadWriteLock`**(共享+独占混合):`state` 用高 16 位计 **读锁计数**,低 16 位计 **写锁重入**;写锁独占,读锁共享,写优/读优通过队列策略控制。 * **`Semaphore`**(共享):`state`=剩余许可,获取时 `CAS(state, state-n)`。 * **`CountDownLatch`**(共享):`state`=计数,`countDown()` 递减到 0 后唤醒所有等待者。 * **`StampedLock`**:不是直接继承 AQS,但思想相近(state+队列),额外有“戳(stamp)”和乐观读校验。 # 四、最小示例:用 AQS 写一个不可重入互斥锁 ```java import java.util.concurrent.locks.*; import java.util.concurrent.atomic.*; import java.util.concurrent.*; public class SimpleMutex implements Lock { private static class Sync extends AbstractQueuedSynchronizer { @Override protected boolean tryAcquire(int arg) { // state=0 -> 1,设置独占线程 if (compareAndSetState(0, 1)) { setExclusiveOwnerThread(Thread.currentThread()); return true; } return false; } @Override protected boolean tryRelease(int arg) { if (getState() == 0 || getExclusiveOwnerThread() != Thread.currentThread()) throw new IllegalMonitorStateException(); setExclusiveOwnerThread(null); setState(0); return true; // 唤醒后继 } @Override protected boolean isHeldExclusively() { return getState() == 1 && getExclusiveOwnerThread() == Thread.currentThread(); } Condition newCondition() { return new ConditionObject(); } } private final Sync sync = new Sync(); @Override public void lock() { sync.acquire(1); } @Override public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } @Override public boolean tryLock() { return sync.tryAcquire(1); } @Override public boolean tryLock(long t, TimeUnit u) throws InterruptedException { return sync.tryAcquireNanos(1, u.toNanos(t)); } @Override public void unlock() { sync.release(1); } @Override public Condition newCondition() { return sync.newCondition(); } } ``` # 五、怎么选哪个锁(实战建议) * **简单同步块** → `synchronized`(首选,语义清晰,JIT 优化成熟)。 * **需要可中断/可超时/公平/多条件队列** → `ReentrantLock`(配 `Condition`)。 * **读多写少** → `ReentrantReadWriteLock`;读极多写极少且读很短 → `StampedLock`(乐观读 + 验证)。 * **限流/并发阈值** → `Semaphore`;**等待 N 个任务** → `CountDownLatch`;**阶段对齐** → `CyclicBarrier/Phaser`。 * **计数/指标** → `LongAdder`;**状态机/指针更新** → 原子类/`VarHandle`。 * **优先避免共享** → `ThreadLocal` / 消息队列(用“消息”代替锁)。 # 六、常见坑点(快速排雷) * `synchronized`/`Lock` **一定 `finally` 解锁**;`Condition.await()` 要配 `while` 重新检查条件。 * **自旋时间过长** 会拖 CPU(让临界区短小,不要持锁做 IO/远程调用)。 * **读锁内升级写锁** 不可行(会死锁);先释放读锁再获取写锁;**写锁降级**可安全:写→读→释放写。 * **`StampedLock` 不可重入**,且不支持 `Condition`;谨慎用于复杂调用链。 * **公平锁** 防饿死但吞吐低于非公平;默认用非公平,极端场景再切换公平。
    发布于 2025-08-19 07:45 •IP属地 河南
    举报
    评论
    收藏