wait/notifyAll 工作不符合预期,请求排查Java多线程代码问题
从你给出的代码片段来看,这个同步机制失效大概率是几个常见的多线程问题导致的,我帮你逐一拆解排查:
1. 缺少对目标对象的notify/notifyAll调用(最可能的原因)
你的confirmMessage代码没写完,但目前看不到对MessageAvailRs对象执行唤醒操作的逻辑。要唤醒send方法里处于wait状态的线程,必须在同一个MessageAvailRs对象的同步块中调用notify()或notifyAll()。
比如正确的confirmMessage应该是这样:
public void confirmMessage(String messageId) { logger.debug("Confirmed message: " + messageId); MessageAvailRs messageAvailRs = messageMap.get(messageId); if (messageAvailRs != null) { synchronized (messageAvailRs) { // 标记消息已确认(根据你的业务逻辑补充) messageAvailRs.setConfirmed(true); // 唤醒等待该对象的线程 messageAvailRs.notify(); // 如果有多个线程可能等待同一个对象,用notifyAll()更稳妥 // messageAvailRs.notifyAll(); } } }
如果没有这一步,send里的线程会一直卡在wait()上,永远不会被唤醒。
2. 未处理wait的虚假唤醒问题
Java的wait()可能会发生虚假唤醒(线程在没有被notify的情况下意外醒来),所以不能直接调用wait(),必须用循环包裹,检查唤醒的条件是否真的满足。
你的send方法应该修改成:
public void send(OTAHotelAvailRS otaHotelAvailRS) throws Exception { MessageAvailRs messageAvailRs = new MessageAvailRs(); // 先标记消息未确认 messageAvailRs.setConfirmed(false); messageMap.put(messageAvailRs.getMessageId(), messageAvailRs); synchronized (messageAvailRs) { messagesSend++; // 循环检查确认状态,避免虚假唤醒 while (!messageAvailRs.isConfirmed()) { messageAvailRs.wait(); } messageWake++; } }
这样即使发生虚假唤醒,线程会再次检查条件,确认消息真的被处理后才会继续执行。
3. messageMap的线程安全性问题
如果messageMap是普通的HashMap这类非线程安全的集合,那么在多线程环境下,send方法的put和confirmMessage的get可能会出现并发问题,导致confirmMessage拿不到正确的MessageAvailRs对象,自然无法唤醒对应的线程。
解决方法是把messageMap换成线程安全的实现,比如ConcurrentHashMap:
private Map<String, MessageAvailRs> messageMap = new ConcurrentHashMap<>();
4. 锁对象的一致性问题
要确保send里wait的对象和confirmMessage里notify的对象是同一个实例。如果messageId对应错了,或者messageMap中存的对象被意外替换,都会导致notify操作作用在错误的对象上,无法唤醒目标线程。
你可以在confirmMessage里加个日志,打印拿到的MessageAvailRs的hashCode,和send方法里存入的对象hashCode对比,排查是否存在对象不匹配的情况。
内容的提问来源于stack exchange,提问作者Fernando Guardiola

