当前位置: 首页 > news >正文

Java CompletableFuture 异步编排实战

## 引言

锁是 Java 并发编程的基础设施。从最古老的 `synchronized` 到 JDK 5 引入的 `ReentrantLock`,再到 JDK 8 的 `StampedLock`,Java 的锁机制经历了从 JVM 内置到 API 化再到乐观读的演进。理解每种锁的原理和适用场景,是写出高效并发代码的前提。

---

## 一、synchronized

### 1.1 基本用法

```java

// 1. 修饰实例方法:锁当前实例

public synchronized void method() {

// 同步代码

}

// 2. 修饰静态方法:锁当前类的 Class 对象

public static synchronized void staticMethod() {

// 同步代码

}

// 3. 修饰代码块:锁指定对象

public void method() {

synchronized (lock) {

// 同步代码

}

}

```

### 1.2 锁的原理:Monitor

```

每个 Java 对象关联一个 Monitor(监视器)

synchronized 进入:

monitorenter → 获取 Monitor 锁

synchronized 退出:

monitorexit → 释放 Monitor 锁

Monitor 结构:

┌────────────────────────────┐

│ _owner → 持有锁的线程 │

│ _entry_set → 等待获取锁 │

│ _wait_set → 调用 wait() │

└────────────────────────────┘

```

### 1.3 锁升级(偏向锁 → 轻量级锁 → 重量级锁)

JDK 6 引入锁升级优化,`synchronized` 不再总是重量级锁:

```

无锁 → 偏向锁 → 轻量级锁 → 重量级锁

│ │ │ │

│ │ │ └─ 竞争激烈,OS 互斥量

│ │ └─ 自旋等待(CAS),适合短时间竞争

│ └─ 同一线程重复获取,无竞争,CAS 都不需要

└─ 对象刚创建,无任何线程访问

升级是单向的(不可降级,G1 下有例外)

```

| 锁状态 | 适用场景 | 获取方式 | 性能 |

|--------|----------|----------|------|

| 偏向锁 | 同一线程反复获取 | 检查偏向线程 ID | 最优 |

| 轻量级锁 | 交替执行,低竞争 | CAS 自旋 | 优 |

| 重量级锁 | 高竞争 | OS 互斥量 | 差 |

### 1.4 优缺点

| 优点 | 缺点 |

|------|------|

| JVM 内置,使用简单 | 无法中断等待锁的线程 |

| 锁升级自动优化 | 无法设置超时 |

| 异常自动释放锁 | 不支持公平锁 |

| 可重入 | 不支持条件变量(Condition) |

| | 在虚拟线程中会导致固定(Pinning) |

---

## 二、ReentrantLock

### 2.1 基本用法

```java

private final ReentrantLock lock = new ReentrantLock();

public void method() {

lock.lock();

try {

// 同步代码

} finally {

lock.unlock(); // 必须在 finally 中释放

}

}

```

### 2.2 公平锁 vs 非公平锁

```java

// 非公平锁(默认):新来的线程可能插队

ReentrantLock unfairLock = new ReentrantLock();

// 公平锁:按等待顺序获取锁

ReentrantLock fairLock = new ReentrantLock(true);

```

| 对比 | 非公平锁 | 公平锁 |

|------|----------|--------|

| 获取顺序 | 可能插队 | FIFO 排队 |

| 吞吐量 | 高 | 低 |

| 饥饿风险 | 有(线程可能永远等不到) | 无 |

| 实现原理 | CAS 直接尝试 | 先入队列排队 |

### 2.3 可中断锁

```java

ReentrantLock lock = new ReentrantLock();

Thread t = new Thread(() -> {

try {

lock.lockInterruptibly(); // 可被中断的锁获取

try {

doWork();

} finally {

lock.unlock();

}

} catch (InterruptedException e) {

System.out.println("被中断,放弃获取锁");

}

});

t.start();

Thread.sleep(1000);

t.interrupt(); // 中断等待锁的线程

```

### 2.4 超时获取锁

```java

ReentrantLock lock = new ReentrantLock();

if (lock.tryLock()) {

try {

doWork();

} finally {

lock.unlock();

}

} else {

System.out.println("获取锁失败,执行降级逻辑");

}

// 带超时

if (lock.tryLock(5, TimeUnit.SECONDS)) {

try {

doWork();

} finally {

lock.unlock();

}

} else {

System.out.println("5秒内未获取锁");

}

```

### 2.5 Condition 条件变量

```java

// synchronized 只有一个 wait set

// ReentrantLock 支持多个 Condition,实现精准唤醒

private final ReentrantLock lock = new ReentrantLock();

private final Condition notFull = lock.newCondition();

private final Condition notEmpty = lock.newCondition();

private final Object[] items = new Object[10];

private int count, putIdx, takeIdx;

public void put(Object item) throws InterruptedException {

lock.lock();

try {

while (count == items.length) {

notFull.await(); // 队列满,等待 notFull 条件

}

items[putIdx] = item;

if (++putIdx == items.length) putIdx = 0;

count++;

notEmpty.signal(); // 通知消费者

} finally {

lock.unlock();

}

}

public Object take() throws InterruptedException {

lock.lock();

try {

while (count == 0) {

notEmpty.await(); // 队列空,等待 notEmpty 条件

}

Object item = items[takeIdx];

items[takeIdx] = null;

if (++takeIdx == items.length) takeIdx = 0;

count--;

notFull.signal(); // 通知生产者

return item;

} finally {

lock.unlock();

}

}

```

### 2.6 synchronized vs ReentrantLock

| 对比 | synchronized | ReentrantLock |

|------|-------------|---------------|

| 实现 | JVM 内置 | JDK API(AQS) |

| 锁获取 | 自动 | 手动 lock/unlock |

| 可中断 | ❌ | ✅ lockInterruptibly |

| 超时 | ❌ | ✅ tryLock(timeout) |

| 公平性 | 非公平 | 可选公平/非公平 |

| 条件变量 | wait/notify(一个) | Condition(多个) |

| 锁升级 | 偏向→轻量→重量 | 无 |

| 虚拟线程 | 固定(Pinning) | 正常卸载 |

| 异常安全 | 自动释放 | 需 finally 手动释放 |

---

## 三、ReadWriteLock

### 3.1 读写分离

```java

// 读读不互斥,读写互斥,写写互斥

private final ReadWriteLock rwLock = new ReentrantReadWriteLock();

private final Lock readLock = rwLock.readLock();

private final Lock writeLock = rwLock.writeLock();

private Map<String, Data> cache = new HashMap<>();

public Data get(String key) {

readLock.lock();

try {

return cache.get(key);

} finally {

readLock.unlock();

}

}

public void put(String key, Data value) {

writeLock.lock();

try {

cache.put(key, value);

} finally {

writeLock.unlock();

}

}

```

### 3.2 ReentrantReadWriteLock 特性

| 特性 | 说明 |

|------|------|

| 公平性 | 支持公平/非公平模式 |

| 重入 | 读锁和写锁都支持重入 |

| 锁降级 | 写锁可降级为读锁(获取写锁→获取读锁→释放写锁) |

| 锁升级 | 读锁不能升级为写锁(会死锁) |

```java

// 锁降级:写锁 → 读锁

writeLock.lock();

try {

updateData();

readLock.lock(); // 获取读锁

} finally {

writeLock.unlock(); // 释放写锁,此时持有读锁

}

try {

readData(); // 在读锁保护下读取

} finally {

readLock.unlock();

}

```

### 3.3 读写锁的问题

- **写饥饿**:读操作频繁时,写线程可能长时间获取不到写锁

- **不支持乐观读**:即使只是读取,也需要获取读锁

---

## 四、StampedLock

### 4.1 核心思想

StampedLock 在 ReadWriteLock 基础上引入了**乐观读(Optimistic Read)**:

```

ReadWriteLock:

读锁 → 获取读锁 → 读取 → 释放读锁(每次都要 CAS)

StampedLock:

乐观读 → 获取戳 → 读取 → 验证戳 → 成功则无需 CAS

→ 失败则升级为读锁重试

```

### 4.2 三种模式

| 模式 | 说明 | 类比 |

|------|------|------|

| 写锁(Writing) | 独占锁,与读锁互斥 | ReadWriteLock 的写锁 |

| 读锁(Reading) | 共享锁,与写锁互斥 | ReadWriteLock 的读锁 |

| 乐观读(Optimistic Reading) | 无锁读取,验证后使用 | 数据库的乐观锁 |

### 4.3 基本用法

```java

private final StampedLock sl = new StampedLock();

private double x, y;

// 写操作

void move(double deltaX, double deltaY) {

long stamp = sl.writeLock();

try {

x += deltaX;

y += deltaY;

} finally {

sl.unlockWrite(stamp);

}

}

// 乐观读

double distanceFromOrigin() {

long stamp = sl.tryOptimisticRead(); // 1. 获取乐观读戳

double currentX = x, currentY = y; // 2. 读取数据(无锁)

if (!sl.validate(stamp)) { // 3. 验证戳(期间是否有写操作?)

stamp = sl.readLock(); // 4. 验证失败,升级为读锁

try {

currentX = x;

currentY = y;

} finally {

sl.unlockRead(stamp);

}

}

return Math.sqrt(currentX * currentX + currentY * currentY);

}

// 悲观读

double distanceFromOriginPessimistic() {

long stamp = sl.readLock();

try {

return Math.sqrt(x * x + y * y);

} finally {

sl.unlockRead(stamp);

}

}

```

### 4.4 锁转换

```java

// 读锁 → 写锁

long stamp = sl.readLock();

try {

long ws = sl.tryConvertToWriteLock(stamp);

if (ws != 0L) {

stamp = ws;

// 持有写锁

} else {

sl.unlockRead(stamp);

stamp = sl.writeLock();

}

} finally {

sl.unlock(stamp);

}

// 乐观读 → 读锁

long stamp = sl.tryOptimisticRead();

if (!sl.validate(stamp)) {

stamp = sl.readLock();

try {

// 重新读取

} finally {

sl.unlockRead(stamp);

}

}

```

### 4.5 StampedLock 注意事项

| 注意 | 说明 |

|------|------|

| 不可重入 | 同一线程不能重复获取同一锁 |

| 不支持 Condition | 没有 newCondition() 方法 |

| 不要用 interrupt | 不要在 readLock/writeLock 中调用 interrupt |

| unlock 必须传 stamp | 传入错误的 stamp 会抛 IllegalMonitorStateException |

| 乐观读适合读多写少 | 写频繁时乐观读经常失败,反而更慢 |

---

## 五、锁选型指南

### 5.1 决策树

```

需要锁吗?

├─ 能否用无锁方案?(Atomic* / Concurrent集合)

│ ├─ 能 → 优先无锁

│ └─ 不能 ↓

├─ 读多写少?

│ ├─ 是 → 读远多于写?

│ │ ├─ 是 → StampedLock(乐观读)

│ │ └─ 否 → ReentrantReadWriteLock

│ └─ 否 ↓

├─ 需要高级功能?(中断/超时/多条件/公平)

│ ├─ 是 → ReentrantLock

│ └─ 否 → synchronized(简单场景优先)

├─ 在虚拟线程中使用?

│ ├─ 是 → ReentrantLock(避免 synchronized 固定)

│ └─ 否 → synchronized

```

### 5.2 性能对比(大致参考)

```

低竞争:synchronized ≈ ReentrantLock > StampedLock

中竞争:StampedLock > ReentrantLock > synchronized

高竞争:StampedLock(乐观读) >> ReentrantReadWriteLock > ReentrantLock > synchronized

读多写少:StampedLock(乐观读) >> ReentrantReadWriteLock >> synchronized

```

### 5.3 速查表

| 场景 | 推荐锁 | 原因 |

|------|--------|------|

| 简单互斥 | synchronized | 简单,JVM 优化好 |

| 需要中断/超时 | ReentrantLock | lockInterruptibly / tryLock |

| 需要公平锁 | ReentrantLock(true) | synchronized 不支持 |

| 需要多条件唤醒 | ReentrantLock + Condition | synchronized 只有一个 wait set |

| 读多写少 | StampedLock | 乐观读无锁,性能最优 |

| 虚拟线程中 | ReentrantLock | synchronized 导致固定 |

| 缓存场景 | ReentrantReadWriteLock | 读写分离 |

| 高并发点数据 | StampedLock | 乐观读 + 锁转换 |

---

## 六、锁的性能优化技巧

### 6.1 减小锁粒度

```java

// ❌ 锁范围过大

synchronized (this) {

validate(data); // 无需锁

process(data); // 需要锁

log(data); // 无需锁

}

// ✅ 只锁必要部分

validate(data);

synchronized (this) {

process(data);

}

log(data);

```

### 6.2 减小锁粒度(分段锁)

```java

// ❌ 一把大锁

synchronized (allAccounts) {

transfer(from, to, amount);

}

// ✅ 分段锁(类似 ConcurrentHashMap)

private final Object[] locks = new Object[16];

{

for (int i = 0; i < locks.length; i++) locks[i] = new Object();

}

void transfer(Account from, Account to, int amount) {

Object lock1 = locks[Math.abs(from.hashCode() % 16)];

Object lock2 = locks[Math.abs(to.hashCode() % 16)];

// 按固定顺序加锁,避免死锁

Object first = System.identityHashCode(lock1) < System.identityHashCode(lock2) ? lock1 : lock2;

Object second = first == lock1 ? lock2 : lock1;

synchronized (first) {

synchronized (second) {

from.debit(amount);

to.credit(amount);

}

}

}

```

### 6.3 避免锁嵌套

```java

// ❌ 锁嵌套,容易死锁

synchronized (lockA) {

synchronized (lockB) {

doWork();

}

}

// ✅ 拆分:先获取所有锁,再操作

// 或使用 tryLock 避免无限等待

if (lockA.tryLock()) {

try {

if (lockB.tryLock()) {

try {

doWork();

} finally {

lockB.unlock();

}

}

} finally {

lockA.unlock();

}

}

```

---

## 总结

| 锁 | 一句话 | 适用场景 |

|----|--------|----------|

| synchronized | 简单可靠,JVM 优化 | 简单互斥,低竞争 |

| ReentrantLock | 功能丰富,API 灵活 | 需要中断/超时/公平/多条件 |

| ReentrantReadWriteLock | 读写分离 | 读多写少,但写不极少 |

| StampedLock | 乐观读,极致性能 | 读远多于写,追求极致吞吐 |

http://www.jsqmd.com/news/1047021/

相关文章:

  • HLA-NoVR终极教程:如何在普通电脑上畅玩《半条命:Alyx》
  • MPLAB代码覆盖率与MISRA检查:嵌入式开发的质量防线实践
  • 欧拉系统上ToDesk Linux客户端的部署与深度配置指南
  • MC9S08SH32硬件断点与调试系统深度解析
  • 【2027最新】基于SpringBoot+Vue的汽车票网上预订系统管理系统源码+MyBatis+MySQL
  • 鸿蒙 Next 相亲防骗雷达 App 开发实战:防骗教育 + 交互式自测 + 内容驱动设计
  • 嵌入式音频设计:I2S/SAI时序解析与低功耗模式实战
  • 免熏蒸木箱个性化方案哪家好? - 工业品牌热点
  • 商洛市2026年最新黄金回收+白银回收+铂金回收+彩金回收门店TOP排行榜+推荐及联系方式+地址+电话+靠谱店铺指南 - 大熊猫898989
  • 呼伦贝尔市2026年最新黄金回收+白银回收+铂金回收+彩金回收门店TOP排行榜+推荐及联系方式+地址+电话+靠谱店铺指南 - 大熊猫898989
  • DeepTutor:你的智能学习伙伴,让AI辅导无处不在
  • Codex 如何使用更高效:一篇讲透实战方法的博文
  • 辽源市2026年最新黄金回收+白银回收+铂金回收+彩金回收门店TOP排行榜+推荐及联系方式+地址+电话+靠谱店铺指南 - 大熊猫898989
  • 南京邮电大学通达学院 光学与光电子基础实验——实验八 声光调制实验【手写报告】
  • b017基于大数据的智能家居销量数据分析-springboot+vue2(设计源文件+万字报告+讲解)(支持资料、图片参考_降重降ai)
  • 商丘市2026年最新黄金回收+白银回收+铂金回收+彩金回收门店TOP排行榜+推荐及联系方式+地址+电话+靠谱店铺指南 - 大熊猫898989
  • 低成本体验Gemini 3.1 Pro完整能力的三层实操路径
  • 2026年批发零售B2B系统推荐:支持多门店/多仓库协同、批量订单处理的工具有哪些?
  • 聊城市2026年最新黄金回收+白银回收+铂金回收+彩金回收门店TOP排行榜+推荐及联系方式+地址+电话+靠谱店铺指南 - 大熊猫898989
  • CatRouter.Net深度测评|国产开源 AI 中转站首选!一键搞定团队多账号精细化管控,告别额度滥用与密钥泄露踩坑!从定价、线路可用率、权限体系到隐藏福利,看完直接省下 90% 选型试错时间!
  • 口碑好的能源煤炭展会策划方有哪些?专业推荐在此 - 工业品牌热点
  • 佛山市2026年最新黄金回收+白银回收+铂金回收+彩金回收门店TOP排行榜+推荐及联系方式+地址+电话+靠谱店铺指南 - 大熊猫898989
  • 常德市本地2026年最新黄金回收靠谱门店TOP排行榜+白银回收+铂金回收+彩金回收及联系方式+地址+电话+诚信店铺推荐 - 盛世金银回收
  • 抖音直播数据采集实战:从零开始构建实时弹幕抓取系统
  • 轻量级智能体实战:3秒响应的端到端图文工作流
  • 临沧市2026年最新黄金回收+白银回收+铂金回收+彩金回收门店TOP排行榜+推荐及联系方式+地址+电话+靠谱店铺指南 - 大熊猫898989
  • Java 异常处理的 8 个常见坑与最佳实践
  • 赤峰市2026年最新黄金回收+白银回收+铂金回收+彩金回收门店TOP排行榜+推荐及联系方式+地址+电话+靠谱店铺指南 - 大熊猫898989
  • 终极指南:在Linux上完美安装Realtek RTL8125 2.5GbE网卡驱动的完整方案
  • 干式喷漆室品牌推荐,众创涂装,水资源紧张地区适用 - 工业品牌热点