【问题标题】:wait / notifyAll not working as expected等待 / notifyAll 没有按预期工作
【发布时间】:2018-04-26 12:58:36
【问题描述】:

我有一个类有两个方法,一个发送消息,另一个确认消息已被接收/处理

   public void send(OTAHotelAvailRS otaHotelAvailRS) throws Exception {
        MessageAvailRs messageAvailRs = new MessageAvailRs();
        messageMap.put(messageAvailRs.getMessageId(), messageAvailRs);
        synchronized (messageAvailRs) {
            messagesSend++;
            messageAvailRs.wait();
            messageWake++;
        }
    }



 public void confirmMessage(String messageId) {
        logger.debug("Confirmed message: " + messageId);
        MessageAvailRs messageAvailRs = messageMap.remove(messageId);
        if (messageAvailRs != null) {
            synchronized (messageAvailRs) {
                messageAvailRs.notifyAll();
                messagesReceived++;
            }
        }
    }

在多线程环境(3 线程 x 100 请求)中运行时,有些消息不会从通知中唤醒..

例如,一旦所有消息都发送完毕

messageSend = 100
messageRec = 100
messageWake = 98

并且map的大小为0,没有重复的messageIds。

我已经缩短了案例。它更复杂。

我有一项服务,每次收到请求时都会调用 send 方法。该方法(不在代码中)将消息发送到 JMS Queue,其他服务接收此消息,处理它们并向另一个 JMSQueue 发送响应,然后 JMSConsumer 读取响应并调用确认方法。

怎么了?

【问题讨论】:

  • 每个线程的作用是什么?是否有多个线程可能同时调用 send 和 receiveMessage?
  • 是的,它们被不同的线程同时调用。每次在服务器上收到请求时都会调用发送。每次处理消息时,JMS 使用者都会调用确认。我错过了一些与将消息发送到 JMS 队列和 JMS 消费者相关的代码
  • 该死!你说的对。这一定是问题所在。我必须检查是否已经收到消息以等待响应。
  • 拥有一个多线程的send,在发送消息后确认自己的消息不是更简单吗?假设发送东西需要时间,您可以使用Executor 清空传出消息队列,并且不会涉及等待。​​

标签: java multithreading concurrency


【解决方案1】:

您错误地使用了wait()

wait 方法应始终处于这样的 while 循环中:

synchronized (obj) {
     while (<condition does not hold>)
         obj.wait();
     ... // Perform action appropriate to condition
 }

假设 Thread1 和 Thread2 正在等待。 Thread3 调用notifyAll。 Thread1 首先唤醒,并消耗这个条件。对于 Thread2 , Thread2 应该再次检查这个条件,如果不满足, Thread2 应该落入另一个wait

【讨论】:

  • 没有等待条件。等待和通知用于同步。
【解决方案2】:

感谢詹姆斯大

问题是 notifyAll() 在 wait() 之前被调用。解决方案是如果收到响应,则使用布尔值不等待()。

 public void send(OTAHotelAvailRS otaHotelAvailRS) throws Exception {
        MessageAvailRs messageAvailRs = new MessageAvailRs();
        messageMap.put(messageAvailRs.getMessageId(), messageAvailRs);
        synchronized (messageAvailRs) {
            messagesSend++;
            if  (!messageAvailRs.isConfirmed()) {
                messageAvailRs.wait();
            }
            messageWake++;
        }
    }

public void confirmMessage(String messageId) {
        logger.debug("Confirmed message: " + messageId);
        MessageAvailRs messageAvailRs = messageMap.remove(messageId);
        if (messageAvailRs != null) {
            meesageAvailRs.confirm();
            synchronized (messageAvailRs) {               
                messageAvailRs.notifyAll();
                messagesReceived++;
            }
        }
    }

public synchronized boolean isConfirmed() {
   return confimed;
}

public synchronized confirm() {
   cofirmed = true;
}

【讨论】:

  • 我建议将if (!messageAvailRs.isConfirmed())改为while (!messageAvailRs.isConfirmed()),正如我在回答中所说的那样。
猜你喜欢
  • 1970-01-01
  • 2018-05-05
  • 2015-05-06
  • 2012-08-15
  • 2021-03-27
  • 2019-04-19
  • 2022-01-06
  • 2020-03-04
  • 2013-12-10
相关资源
最近更新 更多