You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

面试问题:仅用volatile能否实现ReadWriteLock?原子类+volatile方案?

仅用volatile能实现ReadWriteLock吗?

先给明确结论:仅靠volatile变量,不可能实现一个能在多线程并发场景下正确工作的ReadWriteLock。

volatile的作用只是保证变量的可见性和禁止指令重排序,但它不提供原子性——而读写锁的核心逻辑,比如统计活跃读线程的数量、切换读写状态这些操作,都需要原子性来避免竞态条件。

不过你面试时面试官提到的“仅用volatile实现”,大概率是指一种简化到有明显缺陷的“玩具版”读写锁,只能在极低并发(比如最多一个写线程、读线程不会同时抢锁)的场景下勉强跑起来,比如下面这个例子:

class VolatileBasedRWLock {
    // 0=无锁,1=写锁持有,>1=读锁数量
    private volatile int state = 0;

    public void lockRead() {
        // 自旋等写锁释放
        while (state == 1) {
            // 空循环忙等
        }
        // 这里的state++不是原子操作!多线程同时调用会计数错乱
        state++;
    }

    public void unlockRead() {
        state--;
    }

    public void lockWrite() {
        // 自旋等所有读/写锁释放
        while (state != 0) {
            // 空循环忙等
        }
        state = 1;
    }

    public void unlockWrite() {
        state = 0;
    }
}

这个实现的致命问题是state++和state--是拆分的读-改-写操作,完全不原子。当多个读线程同时调用lockRead()时,会出现多个线程同时读到同一个state值,然后各自加1,最终导致读计数被覆盖,写线程可能永远无法获取锁——这显然不是一个可用的读写锁,只是利用volatile的可见性做了状态同步,但核心的原子操作缺失。


结合原子类与volatile实现正确的无锁ReadWriteLock

要做一个能正确工作的无锁读写锁,必须用原子类(比如AtomicInteger)来处理状态的原子更新——其实原子类内部已经用了volatile变量(比如AtomicInteger的value字段就是volatile的),所以本质是用原子类的CAS操作来弥补volatile缺少的原子性。

下面是一个典型的无锁读写锁实现思路:

class AtomicRWLock {
    // 用AtomicInteger的高16位存写锁状态,低16位存读锁计数
    private final AtomicInteger state = new AtomicInteger(0);
    private static final int WRITE_LOCK_FLAG = 0xFFFF0000;
    private static final int READ_LOCK_COUNT_MASK = 0x0000FFFF;
    private static final int READ_LOCK_INCREMENT = 1;
    private static final int WRITE_LOCK_SET = 0x00010000;

    public void lockRead() {
        int currentState;
        do {
            currentState = state.get();
            // 写锁被持有,自旋等待
            if ((currentState & WRITE_LOCK_FLAG) != 0) {
                continue;
            }
            // CAS原子增加读计数,失败就重试
        } while (!state.compareAndSet(currentState, currentState + READ_LOCK_INCREMENT));
    }

    public void unlockRead() {
        // 原子减少读计数
        state.getAndAdd(-READ_LOCK_INCREMENT);
    }

    public void lockWrite() {
        int currentState;
        do {
            currentState = state.get();
            // 有读锁或写锁被持有,自旋等待
            if (currentState != 0) {
                continue;
            }
            // CAS原子设置写锁位,失败就重试
        } while (!state.compareAndSet(currentState, currentState | WRITE_LOCK_SET));
    }

    public void unlockWrite() {
        // 重置状态为无锁
        state.getAndSet(0);
    }
}

这个实现的核心逻辑:

  • 用一个AtomicInteger的高低位分离写锁状态和读锁计数,通过位运算快速判断当前锁状态
  • 读锁获取:先检查写锁是否被占用,再用CAS原子增加读计数,确保多线程下计数正确
  • 写锁获取:检查是否有任何读/写锁在使用,再用CAS原子抢占写锁位
  • 所有状态更新都是原子操作,配合自旋实现无锁等待,同时原子类内部的volatile字段保证了状态的可见性

最后总结
  • 仅用volatile做不出可用的ReadWriteLock,因为没有原子操作支持,必然出现竞态条件
  • 面试官说的“仅用volatile实现”应该是指那种有缺陷的简化版本,只能在极端低并发场景下用,完全不具备生产价值
  • 正确的无锁读写锁必须结合原子类的CAS操作,用原子性更新来避免竞态,同时利用原子类内置的volatile保证状态可见

内容的提问来源于stack exchange,提问作者Dmytro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 13:45:02