Java 为消息队列创建阻塞线程

Java 为消息队列创建阻塞线程,java,multithreading,Java,Multithreading,我正在制作一个简单的服务器客户端应用程序。我以这种方式处理消息队列(class MessageQueue): 这将抛出,这迫使我将wait放入synchronized-我们正处于开始的位置-卡住了。线程不能卡在add方法上,因为messages.wait()会释放对象监视器。这样,当您的特殊线程是wait()ing时,其他线程可以自由进入add()中的同步块(但一次只能进入一个) 为了防止你的“邪恶”的例子,你需要一个保护while循环。如果消费者线程得到通知,但是消息被清空,它将再次注意到wh

我正在制作一个简单的服务器客户端应用程序。我以这种方式处理消息队列(
class MessageQueue
):


这将抛出,这迫使我将
wait
放入
synchronized
-我们正处于开始的位置-卡住了。

线程不能卡在
add
方法上,因为
messages.wait()
会释放对象监视器。这样,当您的特殊线程是
wait()
ing时,其他线程可以自由进入
add()
中的同步块(但一次只能进入一个)

为了防止你的“邪恶”的例子,你需要一个保护while循环。如果消费者线程得到通知,但是
消息
被清空,它将再次注意到while循环和
wait()
中的消息

while(running) {
    synchronized(messages) {
        while(messages.isEmpty())  // Guard against evilness
           try { messages.wait() } catch(InterruptedException e) {}
        // If we get here, messages can't be empty ever
        sendMessage();
    }
}
编辑: 时间线如下所示,Thread1是生产者,Thread2是消费者

Thread1 enters synchronized block.
Thread1 adds an item to messages.
Thread1 calls notify.
Thread1 exits synchronized block.
Thread2 enters synchronized block.
Thread2 checks to see if there are messages, and there are.
Thread2 proceeds to send message.
Thread2 exits synchronized block.


所以,虽然没有人给我一个解决方案,但我一直在测试和思考。如前所述,
wait
call在当前
synchronized
块中释放变量。但还有更多。
Java足够聪明,可以防止冲突,并且不会释放对块中变量执行的其他操作的
同步

我所要做的就是把
wait
while
放在不同的
synchronized
块中。
这是有效的正确代码:

private void readQueue() {
    Log.debug("Waiting for messages to send.");
    while(run) {
        //Calling isEmpty here is a little unsafe but I decided that I don't care
        if(messages.isEmpty()) {
          synchronized(messages) {
            Log.debug("Getting STUCK!");
            //After calling wait, the messages is released, just as expected
            try {messages.wait();}catch(InterruptedException e) {}
          }
        }
        //The sending
        synchronized(messages) {
          //send messages
        }
    }
}
这看起来有点傻,但有道理。考虑旧的、<强>错误的代码< /强>和一个场景:

//Runs from special thread
private void readQueue() {
    while(run) {
        synchronized(messages) {
          //STUCK HERE!
          try {messages.wait();}catch(InterruptedException e) {}
          //send messages
          ...
        }
    }
}
private void evil() {
    synchronized(messages) {
       messages.notify();
       messages.clear(); 
    }
}
  • readQueue
    线程进入
    synchronized
  • readQueue
    线程调用
    messages.wait()
    并释放
    messages
  • 另一个线程运行
    evil()
  • 恶意调用通知并允许
    readQueue
    继续
  • 此时,两个
    已同步的
    块正在对
    消息

  • 我想知道检查是如何实施的,允许采取什么行动。对于这一点,我没有证据(例如,我在文档中找不到任何证据),但第一个代码对我有效,而第二个代码对我无效。

    你可能会觉得有用。我想我会的。但我仍然想知道这些东西是如何协同工作的。你能创造一个新的吗?我复制了你的代码,我没有看到任何线程被卡住。你能做一个示例代码来证明我错了吗?我已经植入了很多调试消息来跟踪什么地方卡住了,我可以看到事情发生在我描述它们的时候。另外,如果事情如您所说,两个并行的
    同步的
    块将运行。(在
    通知
    调用时)。两个同步块(在同一对象监视器上)不能同时运行。不可能。不,不可能。这就是我的原始代码被卡住的原因。因为这是不可能的。是的,两个线程不可能在同步块中同时运行。然而,我的示例代码工作正常,并且保护循环防止了(清空向量的)邪恶。然而,仅仅因为
    wait()
    被释放并不意味着线程可以自由运行,它仍然需要重新获取监视器(因为它仍然在同步块中)。它将在另一个线程通过退出其同步块释放监视器后运行。你欠我+25分,因为我花了这么多时间来教育你:)应该是这样的:4。并允许readQueue在清除消息并释放锁后继续。5a readQueue接受消息锁5b。此时,只有readQueue对消息(可能仍然是空的)进行操作。请参阅我的保护循环示例。@Ishtar No.
    wait
    不会重新初始化锁。而
    notify
    clear
    之前被调用,并被授权解锁阻塞
    wait
    。然而,java不会让这种情况发生。@TomášZato是的!等待并重新初始化锁。”线程t对m'执行n个锁操作。它是在监视器解锁后授予的,因此在调用清除后授予。”但是,请注意,在t完全解锁m的监视器后的一段时间内,u在恢复时的锁定操作无法成功。参见17.2.2项目符号二。
    Thread2 enters synchronized block.
    Thread2 checks to see if there are messages, but there aren't any.
    Thread2 waits() and releases the monitor.
    Thread1 enters synchronized block.
    Thread1 adds an item to messages.
    Thread1 calls notify. (Thread2 is released from wait(), but can't run yet since it needs to acquire the monitor.
    Thread1 exits synchronized block.
    Thread2 acquires the monitor.
    Thread2 checks the loop and notices there is a message.
    Thread2 sends the message.
    Thread2 exits the synchronized block.
    
    private void readQueue() {
        Log.debug("Waiting for messages to send.");
        while(run) {
            //Calling isEmpty here is a little unsafe but I decided that I don't care
            if(messages.isEmpty()) {
              synchronized(messages) {
                Log.debug("Getting STUCK!");
                //After calling wait, the messages is released, just as expected
                try {messages.wait();}catch(InterruptedException e) {}
              }
            }
            //The sending
            synchronized(messages) {
              //send messages
            }
        }
    }
    
    //Runs from special thread
    private void readQueue() {
        while(run) {
            synchronized(messages) {
              //STUCK HERE!
              try {messages.wait();}catch(InterruptedException e) {}
              //send messages
              ...
            }
        }
    }
    private void evil() {
        synchronized(messages) {
           messages.notify();
           messages.clear(); 
        }
    }