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

Java并发:我的读写者问题解决方案为何无法正常工作?

解决读写者问题中写线程无法修改文件内容的问题

看起来你在实现经典读写者问题时踩了几个线程同步的坑,导致写线程要么无法执行修改操作,要么修改内容不符合预期。我来帮你逐一排查并修复这些问题:

核心问题分析

  • 等待线程无法被唤醒:你在startReading和startWriting里用了wait()让线程阻塞,但完全没有对应的notifyAll()来唤醒它们。比如当所有读线程结束后,写线程会一直卡在while(readCount > 0)的循环里,永远不知道可以继续执行了。
  • 写操作硬编码内容:你的startWriting方法里直接写死了file = file + "hi";,完全忽略了传入的edit参数,这就是为什么你看不到预期的写内容。
  • 信号量使用冗余且有泄漏风险:已经用synchronized修饰了方法,相当于已经保证了方法的互斥性,再叠加Semaphore属于重复控制;而且如果semWriter.acquire()后抛出异常,信号量没被释放,后续写线程永远拿不到权限。
  • 状态变更未通知其他线程:readCount或writeCount变化后,没有通知等待的线程,导致它们无法感知状态变化,一直处于阻塞状态。

修复后的完整代码

import java.util.Random;

public class Main {
    public static void main(String[] args) {
        File file = new File("1. Chapter: ");
        Writer w1 = new Writer(file, " w1 ");
        Writer w2 = new Writer(file, " w2 ");
        Reader r1 = new Reader(file);
        Reader r2 = new Reader(file);
        Reader r3 = new Reader(file);
        Reader r4 = new Reader(file);
        Reader r5 = new Reader(file);

        w1.start();
        w2.start();
        r1.start();
        r2.start();
        r3.start();
        r4.start();
        r5.start();

        try {
            w2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-> Final output: " + file.getFile());
    }
}

class File {
    private String file;
    private int readCount;
    private int writeCount;

    public File(String file) {
        this.file = file;
        readCount = 0;
        writeCount = 0;
    }

    public synchronized void startReading() throws InterruptedException {
        // 等待所有写操作完成,用while避免虚假唤醒
        while (writeCount > 0) {
            wait();
        }
        readCount++;
        System.out.println(" --- File was read");
    }

    public synchronized String endReading() {
        String temp = file;
        readCount--;
        // 通知等待的写线程:读线程数量减少了
        notifyAll();
        return temp;
    }

    public synchronized void startWriting(String edit) throws InterruptedException {
        // 等待所有读操作完成
        while (readCount > 0) {
            wait();
        }
        writeCount++;
        System.out.println(" --- File got edited");
        // 使用传入的edit参数修改内容,不再硬编码
        file = file + edit;
    }

    public synchronized void endWriting() {
        writeCount--;
        // 通知等待的读线程:写操作完成了
        notifyAll();
    }

    // 封装file变量,提供安全的获取方法
    public synchronized String getFile() {
        return file;
    }
}

class Writer extends Thread {
    private File file;
    private String edit;

    public Writer(File file, String edit) {
        this.file = file;
        this.edit = edit;
    }

    @Override
    public void run() {
        Random rand = new Random();
        try {
            sleep(1000);
            System.out.println(">W: " + Thread.currentThread().getName() + " started first write.");
            file.startWriting(" first" + edit);
            sleep(3000);
            System.out.println(">W: " + Thread.currentThread().getName() + " ended first write.");
            file.endWriting();

            sleep(2000);
            System.out.println(">W: " + Thread.currentThread().getName() + " started second write.");
            file.startWriting(" second" + edit);
            sleep(3000);
            System.out.println(">W: " + Thread.currentThread().getName() + " ended second write.");
            file.endWriting();

            System.out.println(">W: " + Thread.currentThread().getName() + " finished");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Reader extends Thread {
    private File file;

    public Reader(File file) {
        this.file = file;
    }

    @Override
    public void run() {
        Random rand = new Random();
        try {
            sleep(rand.nextInt(2000));
            System.out.println(">R: " + Thread.currentThread().getName() + " started first read.");
            file.startReading();
            sleep(3000);
            System.out.print(">R: " + Thread.currentThread().getName() + " ended first read: ");
            System.out.println(file.endReading());

            sleep(rand.nextInt(2000));
            System.out.println(">R: " + Thread.currentThread().getName() + " started second read.");
            file.startReading();
            sleep(3000);
            System.out.print(">R: " + Thread.currentThread().getName() + " ended second read: ");
            System.out.println(file.endReading());

            System.out.println(">R: " + Thread.currentThread().getName() + " finished");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

关键修改说明

  1. 移除冗余的Semaphore:synchronized方法已经保证了方法的原子性和互斥性,不需要再用信号量控制计数变量的修改,简化了逻辑。
  2. 添加notifyAll()唤醒等待线程:
    • 读线程结束时调用notifyAll(),告诉等待的写线程可以尝试获取权限了。
    • 写线程结束时调用notifyAll(),告诉等待的读线程可以继续执行了。
  3. 修复写内容硬编码问题:把file = file + "hi";改成file = file + edit;,让写线程使用传入的参数修改内容。
  4. 封装成员变量:将file设为私有,提供getFile()方法获取内容,符合面向对象封装原则,也避免直接访问带来的线程安全隐患。
  5. 用while循环判断等待条件:这是线程等待的最佳实践,防止线程被虚假唤醒后直接执行后续逻辑。
  6. 修正拼写错误:把日志里的startet改成started,输出更规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:31:18