修改Java多线程消息服务器客户端架构后触发IllegalMonitorStateException问题排查
问题原因分析与解决方案
嘿,这个问题的核心其实是调用wait()/notify()时的监视器锁不匹配,我来给你拆解清楚:
为什么会抛出IllegalMonitorStateException?
Java里的wait()和notify()方法有个硬性要求:调用它们的线程必须持有该对象的监视器锁(也就是必须在synchronized(目标对象)块里,并且目标对象就是你调用wait()/notify()的对象)。
咱们对比两段代码的差异:
- 在最初的正常代码里,
writeText()和readText()里的wait()/notify()是在Message类的实例上调用的(默认是this.wait(),this指向Message对象),而Server和Client的run()方法里是synchronized(m)(锁的就是这个Message对象),所以锁对象和调用wait()/notify()的对象完全一致,没有问题。 - 但修改后的代码里,你把
wait()/notify()直接写在了Server1和Client1的run()方法里,这里的wait()默认是this.wait()——也就是当前Server1或Client1线程对象的wait()方法,但你持有的锁是m(Message1对象),这就导致锁对象和调用wait()/notify()的对象不匹配,Java虚拟机就会抛出IllegalMonitorStateException。
修复方案
只需要把wait()和notify()改成在锁对象m上调用就行,也就是把wait()换成m.wait(),notify()换成m.notify()。
修复后的代码如下:
import java.util.Scanner; class Message1{ public String text=""; public boolean hasText; } class Server1 extends Thread{ Message1 m; Server1(Message1 m){ this.m = m; } public void run(){ synchronized(m) { while (!m.text.equals("quit")){ while(m.hasText) try{ m.wait(); }catch (InterruptedException e){ System.out.println(e.toString()); } m.hasText = true; System.out.print("Server wants you to enter a message: "); Scanner sc = new Scanner(System.in); m.text = sc.nextLine(); System.out.println("Server wrote the message: " + m.text); m.notify(); } } } } class Client1 extends Thread{ Message1 m; Client1(Message1 m){ this.m = m; } public void run(){ synchronized (m) { while (!m.text.equals("quit")) { while(!m.hasText) try{ m.wait(); }catch(InterruptedException e){System.out.println(e.toString());} System.out.println("Client read the current message: " + m.text); System.out.println("------------------------------"); m.hasText = false; m.notify(); } } } } class B1{ public static void main(String[] args){ Message1 m1 = new Message1(); new Server1(m1).start(); new Client1(m1).start(); } }
运行这段代码后,就能回到最初的正常输出流程,不会再抛出异常啦。
内容的提问来源于stack exchange,提问作者Saksham Arora
相关产品推荐
相关产品推荐

