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

Java非公平ReentrantReadWriteLock工作原理及公平/非公平模式差异场景咨询

为什么你的ReentrantReadWriteLock公平/非公平模式输出一致?

嘿,我来帮你理清这个问题!你当前的测试场景其实没触碰到ReentrantReadWriteLock两种模式的核心差异点,所以才会看到输出完全一致。咱们一步步拆解:

你的测试场景问题所在

看你的线程启动和时序:

  1. reader2(val=1)先启动,但它会先sleep 3秒,之后才去抢读锁
  2. writer1启动后sleep 1秒,然后去抢写锁——这时候reader1已经持有读锁了(reader1启动后直接抢锁,没有sleep),所以writer1会进入等待队列
  3. reader23秒后醒来抢锁时,writer1已经在等待队列里排队了

不管是公平还是非公平模式,当队列里已经有写线程在等待时,新的读请求都会被阻塞,必须等写线程执行完才能获取读锁——这是ReentrantReadWriteLock为了避免写线程饥饿的设计,所以你的两种模式输出自然一致。

能复现差异的核心场景

要看到两种模式的区别,你需要构造这样的场景:

当有读线程持有读锁时,先启动一个写线程(它会进入等待队列),然后再启动多个新的读线程。此时:

  • 公平模式:新的读线程必须加入等待队列,排在写线程之后,等写线程执行完才能获取读锁
  • 非公平模式:新的读线程可以直接插队,和当前持有读锁的线程共享读锁,无需等待写线程

修改后的测试代码

我调整了你的代码,让它能复现这个差异:

package lockTest;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class MyLockTest {
    // 这里可以切换true(公平)/false(非公平)
    static private ReadWriteLock myLock = new ReentrantReadWriteLock(false);

    public class Reader extends Thread {
        private final String name;
        public Reader(String name) {
            this.name = name;
        }
        public void run() {
            myLock.readLock().lock();
            try {
                System.out.println("Reader [" + name + "] 进入临界区");
                // 持有读锁2秒,模拟业务操作
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println("Reader [" + name + "] 退出临界区");
                myLock.readLock().unlock();
            }
        }
    }

    public class Writer extends Thread {
        private final String name;
        public Writer(String name) {
            this.name = name;
        }
        public void run() {
            myLock.writeLock().lock();
            try {
                System.out.println("Writer [" + name + "] 进入临界区 *****");
                // 持有写锁2秒,模拟业务操作
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println("Writer [" + name + "] 退出临界区 *****");
                myLock.writeLock().unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyLockTest test1 = new MyLockTest();
        
        // 第一步:启动第一个读线程,让它持有读锁
        Reader reader1 = test1.new Reader("R1");
        reader1.start();
        // 确保reader1先拿到读锁
        Thread.sleep(500);
        
        // 第二步:启动写线程,它会进入等待队列
        Writer writer1 = test1.new Writer("W1");
        writer1.start();
        
        // 第三步:连续启动多个新的读线程
        Reader reader2 = test1.new Reader("R2");
        Reader reader3 = test1.new Reader("R3");
        reader2.start();
        reader3.start();
    }
}

两种模式的输出差异

非公平模式(new ReentrantReadWriteLock(false))输出:

Reader [R1] 进入临界区
Reader [R2] 进入临界区
Reader [R3] 进入临界区
Reader [R1] 退出临界区
Reader [R2] 退出临界区
Reader [R3] 退出临界区
Writer [W1] 进入临界区 *****
Writer [W1] 退出临界区 *****

可以看到,R2、R3直接插队获取了读锁,和R1共享,完全无视等待中的W1。

公平模式(new ReentrantReadWriteLock(true))输出:

Reader [R1] 进入临界区
Reader [R1] 退出临界区
Writer [W1] 进入临界区 *****
Writer [W1] 退出临界区 *****
Reader [R2] 进入临界区
Reader [R3] 进入临界区
Reader [R2] 退出临界区
Reader [R3] 退出临界区

公平模式严格按照请求顺序执行:R1先执行,然后是等待队列里的W1,最后才是R2、R3。

总结两种模式的核心区别

  • 公平模式:严格遵循线程请求锁的先后顺序,队列里的线程必须按顺序获取锁,不会出现插队情况,能避免饥饿,但吞吐量会低一些。
  • 非公平模式:允许新的读请求插队获取读锁(即使队列里有写线程等待),写锁释放时也允许新的请求插队,吞吐量更高,但可能导致写线程长时间饥饿(一直有读请求插队,写线程永远得不到执行)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 06:36:53