Java实现兼顾读写公平的读写者问题程序运行卡死问题排查
读写者公平实现代码问题排查与修复
核心问题根源
- 计数器逻辑错误(最核心卡死原因):Reader线程中,读申请阶段仅对
readWait执行了1次自增,但是读结束阶段连续执行了2次readWait.getAndDecrement(),直接导致readWait计数变为负数,后续条件判断完全失效,无法正确唤醒写者线程。 - 等待条件逻辑错误:混淆了「等待线程计数」和「活跃线程计数」的职责:预先定义的
readActive/writeActive两个活跃计数变量完全没有使用,反而在判断阻塞条件时错误使用等待计数作为判断依据,导致线程阻塞逻辑不符合读写者规则。 - 队列锁未正确释放导致死锁:线程获取
queue锁后,如果触发await逻辑会自动释放lock锁,但不会释放已经持有的queue锁,其余所有线程都会卡在queue.lock()步骤无法进入,直接导致全局死锁。 - 通知逻辑错误:当前仅使用
signal()唤醒单个等待线程,存在多个等待线程时无法全部唤醒,比如有多个读者等待时,单次唤醒只能启动一个读者,剩余读者会永久阻塞。
修复方案
- 明确变量职责:
readWait:等待读的线程数readActive:正在读的线程数writeWait:等待写的线程数writeActive:正在写的线程数
- 调整加锁逻辑:
queue锁仅用于控制线程进入等待队列的顺序,拿到进入权限后立即释放,不要持有queue锁进入await逻辑 - 修正计数器操作:读申请时
readWait自增,拿到读权限后readWait自减、readActive自增;读结束时readActive自减 - 修正阻塞条件:
- 读者阻塞条件:存在活跃写者
writeActive.get()>0或者 存在等待写者writeWait.get()>0(保证公平,不让写者饥饿) - 写者阻塞条件:存在活跃读者
readActive.get()>0或者 存在活跃写者writeActive.get()>0
- 读者阻塞条件:存在活跃写者
- 替换
signal()为signalAll(),确保所有符合条件的等待线程都能被唤醒
修复后核心代码示例
public class ReaderWriter { static Lock lock = new ReentrantLock(); static Lock queue = new ReentrantLock(); static Condition read = lock.newCondition(); static Condition write = lock.newCondition(); static AtomicInteger readWait = new AtomicInteger(0); static AtomicInteger readActive = new AtomicInteger(0); static AtomicInteger writeWait= new AtomicInteger(0); static AtomicInteger writeActive = new AtomicInteger(0); static StringBuilder str = new StringBuilder(); static int val = 0; static class ReaderThread extends Thread { @Override public void run() { try { // queue锁仅控制排队顺序,不持有进入后续逻辑 queue.lock(); lock.lock(); readWait.getAndIncrement(); queue.unlock(); // 有写者活跃或等待时阻塞,保证公平 while (writeActive.get() > 0 || writeWait.get() > 0 ) read.await(); readWait.getAndDecrement(); readActive.getAndIncrement(); lock.unlock(); // 模拟读操作 System.out.println("Thread: " + Thread.currentThread().getId() + " is reading a string: " + str); Thread.sleep(1500); System.out.println("Thread: " + Thread.currentThread().getId() + " has stopped reading"); lock.lock(); readActive.getAndDecrement(); // 没有活跃读者时唤醒写者 if (readActive.get() == 0 ) { write.signalAll(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } static class WriterThread extends Thread { @Override public void run() { try { queue.lock(); lock.lock(); writeWait.getAndIncrement(); queue.unlock(); // 有活跃读者或写者时阻塞 while(readActive.get() > 0 || writeActive.get() > 0) write.await(); writeWait.getAndDecrement(); writeActive.getAndIncrement(); lock.unlock(); // 模拟写操作 System.out.println("Thread: " + Thread.currentThread().getId() + " is writing"); str.append(val); val++; Thread.sleep(2000); System.out.println("Thread: " + Thread.currentThread().getId() + " has stopped writing"); lock.lock(); writeActive.getAndDecrement(); // 优先唤醒等待的写者,没有则唤醒读者 if(writeWait.get() > 0) { write.signalAll(); } else { read.signalAll(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } }
内容的提问来源于stack exchange,提问作者krysznys
相关产品推荐
相关产品推荐

