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

Java实现读者-写者问题遇异常:写者并发执行、程序无法终止及ExecutorService使用疑问

读者-写者问题的BUG修复与正确实现

看起来你遇到的问题主要来自几个关键的代码设计失误,咱们一步步拆解并修复:

核心问题分析

1. 致命的命名冲突

你的ReadWriteLock类里,方法名readLock()、writeLock()和对应的Semaphore变量名完全一致!在Java中,当方法和字段同名时,方法内部直接调用名称会优先指向方法而非字段——这意味着你根本没有真正获取Semaphore的互斥锁!这就是为什么多个写者能同时执行写入操作的根本原因,互斥机制完全失效了。

2. 无限循环导致程序无法终止

Writer和Reader的run()方法都是while(true)的死循环,线程永远不会结束,ExecutorService自然无法正常终止,程序会一直挂着。

3. 写操作的时机逻辑错误

原writeLock()方法里,你在释放写锁之后才打印"finished WRITING",这会造成视觉混淆:前一个写者刚释放锁,后一个写者立刻获取锁开始写入,而前一个的完成信息还没输出,看起来就像两个写者同时在操作。

4. 未正确关闭线程池

main方法提交任务后没有调用shutdown(),也没有等待任务完成,即使线程执行完毕,ExecutorService也不会自动关闭。


修复后的完整代码

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class ReaderWriter {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        ReadWriteLock rwLock = new ReadWriteLock();

        // 每个写者/读者执行3次操作后退出
        for (int i = 0; i < 4; i++) {
            executorService.execute(new Writer(rwLock, 3));
            executorService.execute(new Reader(rwLock, 3));
        }

        executorService.shutdown(); // 停止接受新任务
        try {
            // 等待所有任务完成,最多等待30秒
            if (!executorService.awaitTermination(30, TimeUnit.SECONDS)) {
                executorService.shutdownNow(); // 超时强制终止
            }
        } catch (InterruptedException e) {
            executorService.shutdownNow();
        }
    }
}

class ReadWriteLock {
    // 重命名信号量,避免与方法名冲突
    private final Semaphore readSemaphore = new Semaphore(1);
    private final Semaphore writeSemaphore = new Semaphore(1);
    private volatile int readCount = 0; // 无需static,所有线程共享同一个实例

    public void acquireReadLock() throws InterruptedException {
        readSemaphore.acquire();
        readCount++;
        if (readCount == 1) {
            // 第一个读者抢占写锁,阻止写者进入
            writeSemaphore.acquire();
        }
        readSemaphore.release();

        // 读取操作逻辑
        System.out.println("Thread " + Thread.currentThread().getName() + " is READING");
        Thread.sleep(1500);
        System.out.println("Thread " + Thread.currentThread().getName() + " has FINISHED READING");

        readSemaphore.acquire();
        readCount--;
        if (readCount == 0) {
            // 最后一个读者释放写锁,允许写者进入
            writeSemaphore.release();
        }
        readSemaphore.release();
    }

    public void acquireWriteLock() throws InterruptedException {
        writeSemaphore.acquire();

        // 写入操作逻辑
        System.out.println("Thread " + Thread.currentThread().getName() + " is WRITING");
        Thread.sleep(2500);
        System.out.println("Thread " + Thread.currentThread().getName() + " has FINISHED WRITING");

        writeSemaphore.release(); // 所有写操作完成后再释放锁
    }
}

class Writer implements Runnable {
    private final ReadWriteLock rwLock;
    private final int executeTimes; // 控制执行次数,避免死循环

    public Writer(ReadWriteLock rwLock, int executeTimes) {
        this.rwLock = rwLock;
        this.executeTimes = executeTimes;
    }

    @Override
    public void run() {
        for (int i = 0; i < executeTimes; i++) {
            try {
                rwLock.acquireWriteLock();
                Thread.sleep(500); // 模拟操作间隔
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                System.out.println("Writer thread " + Thread.currentThread().getName() + " was interrupted");
                break;
            }
        }
    }
}

class Reader implements Runnable {
    private final ReadWriteLock rwLock;
    private final int executeTimes;

    public Reader(ReadWriteLock rwLock, int executeTimes) {
        this.rwLock = rwLock;
        this.executeTimes = executeTimes;
    }

    @Override
    public void run() {
        for (int i = 0; i < executeTimes; i++) {
            try {
                rwLock.acquireReadLock();
                Thread.sleep(500);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                System.out.println("Reader thread " + Thread.currentThread().getName() + " was interrupted");
                break;
            }
        }
    }
}

关键修改说明

  1. 修复命名冲突:将Semaphore变量重命名为readSemaphore和writeSemaphore,方法名改为acquireReadLock()/acquireWriteLock(),确保正确调用信号量的锁方法,实现真正的互斥。
  2. 移除死循环:给每个线程添加执行次数参数,让线程完成指定操作后自动退出,保证程序能正常终止。
  3. 修正写锁释放时机:将写锁释放操作放在所有写入逻辑之后,避免视觉上的并发误解。
  4. 正确管理线程池:调用shutdown()和awaitTermination(),确保线程池能在任务完成后正常关闭。
  5. 移除不必要的static:所有线程共享同一个ReadWriteLock实例,成员变量无需static,避免全局共享的意外问题。

这样修改后,程序就能严格遵循读者-写者规则:同一时间仅一个写者执行,无写者时多个读者可并发读取,且程序能正常终止。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 03:32:37