Java使用wait()和notify()实现两个线程交替持有共享Ball对象问题
问题原因分析
- 日志打印逻辑错误:
takeBall()方法中刚进入方法就打印「拿到球」的日志,此时还未校验球的占用状态,并未真正获取到球的持有权,会出现日志和实际执行状态不符的问题。 - 锁竞争优先级问题:Java内置的
synchronized锁是非公平锁,刚释放锁的线程再次申请锁时,大概率会比已经被唤醒的等待线程先抢到锁。你当前的逻辑中,线程调用完dropBall()释放锁后会立刻进入下一轮循环申请takeBall(),会频繁抢到锁,导致被notify()唤醒的另一个线程拿到锁时,球已经再次被前一个线程占用,只能继续进入等待状态。
解决方案
可以在Ball类中新增变量记录上一次持有球的线程,校验占用状态时增加判断,禁止同一个线程连续持有球,强制两个线程交替执行,同时调整日志打印位置到真正获取到球之后,修正后的Ball类代码如下:
public class Ball{ boolean ballInUse = false; // 新增字段记录上一次持有球的线程名 String lastHolder = ""; public synchronized void takeBall() throws InterruptedException { // 等待条件:球被占用 或 上一次持有者是当前线程,就继续等待 while (ballInUse || lastHolder.equals(Thread.currentThread().getName())){ wait(); } ballInUse = true; lastHolder = Thread.currentThread().getName(); // 真正拿到球后再打印日志 System.out.println("Ball taken by -> "+Thread.currentThread().getName()); } public synchronized void dropBall() throws InterruptedException { System.out.println("Ball dropped by -> "+Thread.currentThread().getName()); ballInUse = false; notify(); } }
逻辑说明
新增lastHolder字段后,就算前一个线程释放锁后再次抢到了takeBall()的锁,也会因为「上一次持有者是自己」的校验不通过,主动进入等待状态,把持有权让给另一个线程,完美实现交替持有的要求。
内容的提问来源于stack exchange,提问作者Iosiv
相关产品推荐
相关产品推荐

