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; } } } }
关键修改说明
- 修复命名冲突:将Semaphore变量重命名为
readSemaphore和writeSemaphore,方法名改为acquireReadLock()/acquireWriteLock(),确保正确调用信号量的锁方法,实现真正的互斥。 - 移除死循环:给每个线程添加执行次数参数,让线程完成指定操作后自动退出,保证程序能正常终止。
- 修正写锁释放时机:将写锁释放操作放在所有写入逻辑之后,避免视觉上的并发误解。
- 正确管理线程池:调用
shutdown()和awaitTermination(),确保线程池能在任务完成后正常关闭。 - 移除不必要的static:所有线程共享同一个
ReadWriteLock实例,成员变量无需static,避免全局共享的意外问题。
这样修改后,程序就能严格遵循读者-写者规则:同一时间仅一个写者执行,无写者时多个读者可并发读取,且程序能正常终止。
内容的提问来源于stack exchange,提问作者Naragoza
相关产品推荐
相关产品推荐

