Java多线程实现读写者问题时Thread.sleep阻塞读线程问题咨询
问题根因定位
你遇到的首个读者休眠期间其余读者无法进入的问题,本质是读操作错误加了排他锁,或者读者计数的同步逻辑范围错误,常见错误场景包括:
- 直接给读方法加了
synchronized修饰,导致同一时间仅能有一个线程进入读方法 - 误将实际读操作代码块包在了读者计数更新的同步块中,第一个读者持有同步锁休眠,其余读者无法更新计数获取读权限
读写者问题核心实现逻辑(读者优先版本示例)
核心依赖3个变量:
int readCount:记录当前正在执行读操作的线程数,初始为0ReentrantLock countLock:仅用于保护readCount计数更新的线程安全ReentrantLock writeLock:实现读写互斥、写写互斥的全局锁
读操作核心代码
public void read() throws InterruptedException { // 仅更新计数时加计数锁 countLock.lock(); try { readCount++; // 第一个读者获取写锁,阻塞所有写线程 if (readCount == 1) { writeLock.lock(); } } finally { countLock.unlock(); } // 实际读逻辑完全在同步块之外,多个读者可同时执行 try { System.out.println(Thread.currentThread().getName() + " 开始读取数据"); Thread.sleep(5000); // 此处休眠不会阻塞其他读者进入 System.out.println(Thread.currentThread().getName() + " 读取数据完成"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // 读结束更新计数 countLock.lock(); try { readCount--; // 最后一个读者释放写锁,允许写线程进入 if (readCount == 0) { writeLock.unlock(); } } finally { countLock.unlock(); } } }
写操作核心代码
public void write() throws InterruptedException { writeLock.lock(); try { System.out.println(Thread.currentThread().getName() + " 开始写入数据"); Thread.sleep(3000); System.out.println(Thread.currentThread().getName() + " 写入数据完成"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { writeLock.unlock(); } }
实现正确性校验点
你可以对照自己的代码逐项检查:
- 实际读逻辑是否在
countLock的同步块之外,读操作期间不会持有任何排他锁 - 所有锁的释放逻辑都放在
finally块中,避免异常导致锁泄漏引发死锁 - 如果使用
Object.wait/notify实现,条件判断必须放在循环中,处理虚假唤醒场景 - 捕获
InterruptedException后正确恢复了线程中断状态,没有吞中断异常
优化方向
- 解决读者饥饿问题:默认读者优先实现中如果读线程持续存在,写线程会永远抢不到锁,可以切换为公平锁模式,或者实现写者优先、公平读写的版本
- 性能优化:Java 8及以上版本可以用
StampedLock替代普通重入锁,支持乐观读模式,读多写少场景下吞吐量提升非常明显 - 增加超时机制:加锁时设置超时时间,避免极端场景下的死锁问题
- 增加可观测性:埋点统计当前读线程数、等待的读写线程数,方便线上问题排查
内容的提问来源于stack exchange,提问作者rsaraf97
相关产品推荐
相关产品推荐

