Java 显式锁的高级扩展:ReadWriteLock、StampedLock 与 LockSupport

在前两篇文章中,我们已经深入解析了 Java 内置锁 synchronized 与显式锁 ReentrantLock
然而在高并发场景下,某些复杂需求仅靠这两种锁还不足以满足:

  • 如何在 读多写少 的业务场景下提高并发度?
  • 如何在 乐观并发 下减少锁竞争?
  • 如何实现 底层线程阻塞/唤醒 的基础设施?

为了解决这些问题,JUC 提供了 ReadWriteLock、StampedLock、LockSupport 等高级锁机制。本文将对它们进行系统性解析。


一、ReadWriteLock:读写分离的锁机制

1.1 基本概念

ReadWriteLock 是一种 读写分离锁,核心思想是:

  • 多个读线程可以并发访问(读共享);
  • 写线程独占锁,写时禁止任何读写操作;
  • 读写互斥、写写互斥、读读不互斥

1.2 实现类:ReentrantReadWriteLock

JUC 提供的主要实现是 ReentrantReadWriteLock

ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock readLock = rwLock.readLock();
Lock writeLock = rwLock.writeLock();

1.3 使用示例

class SafeCache {
    private final Map<String, String> cache = new HashMap<>();
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final Lock readLock = rwLock.readLock();
    private final Lock writeLock = rwLock.writeLock();

    public String get(String key) {
        readLock.lock();
        try {
            return cache.get(key);
        } finally {
            readLock.unlock();
        }
    }

    public void put(String key, String value) {
        writeLock.lock();
        try {
            cache.put(key, value);
        } finally {
            writeLock.unlock();
        }
    }
}

1.4 特性

  • 可重入性:与 ReentrantLock 一样支持可重入。
  • 公平性:支持公平/非公平模式。
  • 降级:支持从写锁降级为读锁(但不支持读锁升级为写锁)。

1.5 适用场景

  • 读多写少,如缓存系统、本地配置读取。
  • 不适合写操作频繁的场景,否则读线程会频繁阻塞。

二、StampedLock:乐观读的锁机制

2.1 背景

虽然 ReentrantReadWriteLock 能解决读多写少问题,但在 读多写极少 的极端场景下,读写互斥仍然会导致性能浪费。
为此 JDK 8 引入了 StampedLock,提供 乐观读(Optimistic Reading)

2.2 基本用法

StampedLock lock = new StampedLock();

// 乐观读
long stamp = lock.tryOptimisticRead();
int curX = x;
int curY = y;
if (!lock.validate(stamp)) {
    // 乐观读失败,退化为悲观读
    stamp = lock.readLock();
    try {
        curX = x;
        curY = y;
    } finally {
        lock.unlockRead(stamp);
    }
}

2.3 特性

  • 乐观读:无锁访问,性能极高,但需要校验。
  • 悲观读/写锁:类似 ReentrantReadWriteLock
  • 不可重入:不同于 ReentrantLock,StampedLock 不支持可重入。
  • 锁升级/降级:支持写锁降级为读锁。

2.4 适用场景

  • 大量只读、极少写操作的应用。
  • 对延迟敏感的高并发业务,如金融行情快照、缓存查询。

三、LockSupport:底层线程阻塞工具

3.1 定义

LockSupport 是 JUC 提供的 基础线程阻塞工具类,为构建各种锁和并发工具提供底层支持。
它比 Object.wait/notify 更灵活,避免了“先通知再等待”的经典问题。

3.2 常用方法

  • 阻塞线程

    LockSupport.park();
    LockSupport.parkNanos(1000000L);
    
  • 唤醒线程

    LockSupport.unpark(thread);
    

3.3 特性

  • 许可证模型:每个线程维护一个“许可位”,默认 0。

    • unpark() 设置许可位为 1;
    • park() 消费许可位(若无许可则阻塞)。
  • 无条件唤醒丢失:即使先调用 unpark(),再调用 park(),线程也不会阻塞。

3.4 应用场景

  • AQS 内部实现(ReentrantLock、Semaphore、CountDownLatch 等的基础)。
  • 自定义并发控制工具。

四、三者比较

特性 ReentrantReadWriteLock StampedLock LockSupport
读写分离 支持 支持(且有乐观读) 不支持
可重入性 支持 不支持 不涉及锁
乐观锁 不支持 支持 不支持
公平性选择 支持 不支持 不涉及
使用场景 读多写少 读多写极少、延迟敏感 底层工具,构建并发组件基础

五、工程实践建议

  1. 优先选择 ReentrantReadWriteLock

    • 在一般的读多写少场景下,语义清晰,性能稳定。
  2. 极致性能选择 StampedLock

    • 在读远大于写、对延迟极度敏感的场景下使用。
    • 注意其不可重入特性,使用时要小心避免死锁。
  3. LockSupport 不直接用于业务代码

    • 它是底层工具类,适合用于实现自定义同步器。
    • 在业务开发中,尽量使用上层 API(如 ReentrantLock、Semaphore)。

六、总结

  • ReentrantReadWriteLock:读写分离,适合常见的读多写少场景。
  • StampedLock:引入乐观读机制,在读极多写极少场景下性能更佳。
  • LockSupport:并发工具的底层基石,用于构建更复杂的同步器。

至此,Java 锁机制的主流体系已经完整覆盖:

  • synchronized —— 内置锁,语义清晰,现代 JDK 已优化。
  • ReentrantLock —— 显式锁,灵活可控。
  • ReentrantReadWriteLock —— 读写分离,提升读性能。
  • StampedLock —— 乐观读,适合高读低写。
  • LockSupport —— 低层支撑,构建并发工具的基础。

一句话总结:
Java 的锁体系是一座分层架构,从基础的互斥语义到高阶的并发优化,再到底层的线程阻塞原语,构成了完整的并发编程工具链。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐