Java wait/notify实现奇偶交替打印报IllegalMonitorStateException问题
问题解答
- 不存在“for循环中不允许使用synchronized块”的限制,你遇到的异常和synchronized块的位置无关。
IllegalMonitorStateException的触发原因非常明确:调用对象的wait()/notify()/notifyAll()方法时,当前线程没有持有该对象的监视器锁。
根因细节
你的代码中自定义了专门的锁对象private Object lock = new Object();,同步块也确实是对lock加锁:
synchronized (lock){ // 临界区代码 }
但你在临界区内直接调用了无前缀的wait()和notifyAll(),这种写法等价于调用this.wait()和this.notifyAll()——也就是你尝试操作的是当前OddEvenPrinter实例的等待队列,但你持有的是lock对象的锁,从来没有获取过当前实例的监视器锁,自然会抛出非法监视器状态异常。
Java 多线程 wait/notify 机制的强制规则:任何对象的wait、notify、notifyAll方法,都必须在持有该对象自身监视器锁的上下文(即synchronized(该对象)修饰的代码块/方法)中调用,否则直接抛出
IllegalMonitorStateException。
额外隐藏问题
你的代码除了wait/notify调用对象错误之外,还有一处逻辑漏洞会导致无法实现交替打印:打印完成、唤醒等待线程前,没有切换printFlag标志位的状态。就算修正了锁调用问题,打印完奇数后printFlag仍为false,下一轮循环奇数线程不会进入等待状态,会连续打印奇数,完全达不到交替效果。
修正后可运行代码
public class OddEvenPrinter { private Object lock = new Object(); private boolean printFlag = false; void printOdd() throws InterruptedException { for(int i=1; i < 25; i=i+2){ synchronized (lock){ while(printFlag){ // 调用当前持有锁的lock对象的wait方法 lock.wait(); } System.out.println(i + "******** PRINT ODD "); // 切换标志位,标记下次该打印偶数 printFlag = true; // 唤醒在lock对象上等待的偶数打印线程 lock.notifyAll(); } } } void printEven() throws InterruptedException { for(int i=0; i < 25; i=i+2){ synchronized (lock){ while(!printFlag){ lock.wait(); } System.out.println(i + "******** PRINT EVEN "); // 切换标志位,标记下次该打印奇数 printFlag = false; lock.notifyAll(); } } } public static void main(String[] args) throws InterruptedException { OddEvenPrinter printer = new OddEvenPrinter(); Thread a = new Thread(new Runnable() { @Override public void run() { try { printer.printEven(); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); Thread b = new Thread(new Runnable() { @Override public void run() { try { printer.printOdd(); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); a.start(); b.start(); // 等待两个工作线程执行完成再打印结束提示 a.join(); b.join(); System.out.println("All DONE....."); } }
内容的提问来源于stack exchange,提问作者Vineel
相关产品推荐
相关产品推荐

