分布式网络节点开发中wait()引发的空指针及非法监视器异常排查
问题分析与修复方案
服务器端IllegalMonitorStateException根源与修复
你在synchronized(lock)代码块里直接调用wait(),这其实是调用当前对象(this)的wait()方法,但当前线程持有的是lock对象的监视器锁,不是this的,所以必然抛出IllegalMonitorStateException——这就是服务器端报错的直接原因。
修复很简单:必须调用同步锁对象的wait(),也就是把wait()改成lock.wait()。
修改后的等待代码:
while (node == 1) { synchronized (lock) { try { lock.wait(); // 改成lock.wait(),和同步对象对应 System.out.println("I waited."); node = 2; } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println("This is a thread error."); } } System.out.println("And the Wait Ended."); response = Response.newBuilder() .setResponseType(Response.ResponseType.REPLY) .setMessage("You waited.") .build(); // 别忘了把响应发给客户端! response.writeDelimitedTo(out); }
客户端空指针异常的解决
客户端的NullPointerException是因为:服务器端进入等待状态后,没有向客户端发送第二个响应,客户端调用Response.parseDelimitedFrom(in)时,输入流没有数据,方法返回null,后续调用response.getMessage()自然触发空指针。
两步修复:
- 客户端读取响应前先判空,避免直接调用方法;
- 服务器端唤醒后必须把响应写入输出流发给客户端(上面的代码已经加了
response.writeDelimitedTo(out))。
修改后的客户端代码:
try { // connect to the server serverSock = new Socket(host, port); // write to the server out = serverSock.getOutputStream(); in = serverSock.getInputStream(); op.writeDelimitedTo(out); // read from the server response = Response.parseDelimitedFrom(in); // 先检查响应是否有效 if (response != null) { System.out.println(response.getMessage()); } else { System.err.println("服务器未返回有效响应"); return; } // wait on the server response = Response.parseDelimitedFrom(in); if (response != null) { System.out.println(response.getMessage()); } else { System.err.println("服务器未返回等待后的响应"); } } catch (Exception e) { e.printStackTrace(); } finally { // 关闭资源 if (in != null) in.close(); if (out != null) out.close(); if (serverSock != null) serverSock.close(); }
额外提醒
- 确保
lock是所有处理该客户端的线程共享的实例,每个线程新建lock的话,同步和等待唤醒完全没用; - 别用忙等待,
wait()/notifyAll()是正确的线程协作方式,修复监视器问题后就能正常工作; - 你处理
InterruptedException时恢复中断状态的代码是对的,不用改。
内容的提问来源于stack exchange,提问作者Nehemiah Newell
相关产品推荐
相关产品推荐

