尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Java CompletableFuture 异步编排实战

Java CompletableFuture 异步编排实战
📅 发布时间:2026/6/20 7:19:37

## 引言

锁是 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 | 乐观读,极致性能 | 读远多于写,追求极致吞吐 |

相关新闻

  • DeepTutor:你的智能学习伙伴,让AI辅导无处不在
  • 鸿蒙 Next 相亲防骗雷达 App 开发实战:防骗教育 + 交互式自测 + 内容驱动设计
  • 免熏蒸木箱个性化方案哪家好? - 工业品牌热点

最新新闻

  • 从Sentinel-2 L1C数据到物理量:手把手解析辐亮度与TOA反射率的关键公式与参数
  • 2026年临沧市老百姓优先选择的五家贵金属回收门店 黄金回收白银回收铂金回收彩金回收合规靠谱门店测评合集+联系方式 - 亦辰小黄鸭
  • 嵌入式Linux:镜像、分区与文件系统:.img 到底是什么
  • 2026年淮安市贵金属旧料回收优质靠谱实体门店精选五家 黄金回收铂金回收白银回收彩金回收真实探店测评清单及联系方式推荐 - 前途无量YY
  • 2026年达州市贵金属旧料回收优质靠谱实体门店精选五家 黄金回收铂金回收白银回收彩金回收真实探店测评清单及联系方式推荐 - 前途无量YY
  • 2026年临汾市老百姓优先选择的五家贵金属回收门店 黄金回收白银回收铂金回收彩金回收合规靠谱门店测评合集+联系方式 - 亦辰小黄鸭

日新闻

  • 信任的进化:技术实现详解——如何用JavaScript构建博弈论模拟器
  • Terrakube自定义工作流:如何集成OPA、Infracost等工具扩展IaC能力
  • grunt-concurrent快速入门:5分钟学会并行运行Grunt任务

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号