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

ReadWriteLock的作用:防止异常还是避免多线程读写数据混乱?

多线程文件并发读写:为什么没抛异常?ReadWriteLock到底管啥?

先直接给你答案:ReadWriteLock主要是用来避免多线程互相干扰导致的数据错乱(比如文本乱码、内容拼接错误),而不是单纯为了防止异常。你的测试没抛异常只是巧合,不代表这种写法安全。

为啥你的代码没炸?

Java的FileWriter和FileReader本身完全不是线程安全的,但没抛出异常不代表没问题:

  • 底层操作系统的文件操作有一点点原子性保障(比如单次写少量数据时,OS可能帮你"扛"了一部分冲突),但这完全依赖系统实现,不是Java给你的承诺。
  • 你每次只写固定的"foo\n"还调用了flush(),这种场景下冲突概率比较低,但不代表不会出现——要是你多跑几次测试,或者把写入内容改长、线程数调大,大概率会看到文件里的内容乱掉,甚至可能出现IO异常(只是你没碰到而已)。

ReadWriteLock的核心价值

ReadWriteLock是读写分离的锁,它解决的核心问题是数据一致性和读写性能平衡:

  • 写操作必须互斥:同一时间只能有一个线程写,不然两个线程同时写的内容会互相穿插、覆盖,比如本来要写两行"foo",结果变成"ffooo"或者其他乱码。
  • 读操作可以共享:多个线程同时读不会互相阻塞,这在读多写少的场景下比单纯用synchronized效率高很多。
  • 它的目标是保证你读写的内容是"正确"的,而不是为了抓异常——异常只是并发冲突可能带来的副作用之一,不是全部。

你的代码里的坑

看你的测试代码,还有几个明显的线程不安全点:

  • 所有线程共享同一个FileWriter和FileReader实例,这俩类根本没考虑多线程场景,就算没抛异常,也极大概率出现:
    • 写的时候内容乱序拼接,比如两个线程的"foo"粘在一起。
    • 读的时候读到半拉内容,比如一个线程刚写了"fo",另一个线程就把这俩字符读走了。
  • 所有线程跑完才关流,这也有风险——万一某个线程还在操作流,你就给关了,说不定下次跑就出异常了。

怎么改才安全?

要做安全的并发文件读写,必须加锁:

  • 用ReadWriteLock(或者synchronized,但ReadWriteLock在多读场景下更高效)来包裹读写操作:
    • 写的时候拿写锁,写完立刻释放(一定要放finally里,防止异常导致锁没释放)。
    • 读的时候拿读锁,读完释放。

给你改个简单的例子,把Worker类加上锁:

static class Worker implements Runnable {
    private final Reader rdr;
    private final Writer wrtr;
    private final int nmbRdWrt, maxWaitTime;
    private final ReadWriteLock rwLock;

    public Worker(Reader rdr, Writer wrtr, int nmbRdWrt, int maxWaitTime, ReadWriteLock rwLock) {
        this.rdr = rdr;
        this.wrtr = wrtr;
        this.nmbRdWrt = nmbRdWrt;
        this.maxWaitTime = maxWaitTime;
        this.rwLock = rwLock;
    }

    public void run() {
        try {
            for(int i = 0; i < nmbRdWrt; i++) {
                rndmPause(maxWaitTime);
                // 写操作加写锁,确保同一时间只有一个线程写
                rwLock.writeLock().lock();
                try {
                    wrtr.write("foo" + System.getProperty("line.separator"));
                    wrtr.flush();
                } finally {
                    rwLock.writeLock().unlock();
                }
                rndmPause(maxWaitTime);
                // 读操作加读锁,允许多个线程同时读
                rwLock.readLock().lock();
                try {
                    char[] cbuf = new char[100];
                    rdr.read(cbuf);
                } finally {
                    rwLock.readLock().unlock();
                }
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

然后在Main方法里初始化锁,传给每个Worker:

ReadWriteLock rwLock = new ReentrantReadWriteLock();
// 创建Worker时把锁传进去
Thread t = new Thread(new Worker(rdr, wrtr, nmbOfReadWritePerThread, maxWaitTimeBetweenReadWrite, rwLock));

这样改完,就能保证读写操作的安全性,不会出现内容错乱,也能降低异常出现的概率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:39:22