为消息队列创建阻塞线程

时间:2014-04-25 10:30:15

标签: java multithreading

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

private Vector<String> messages;
//Runs from any thread
public void add(String message) {
    synchronized(messages) {
      messages.add(message);
      //This is only way to unstuck messages.wait()
      messages.notifyAll();
    }
}
//Runs from special thread
private void readQueue() {
    Log.debug("Waiting for messages to send.");
    while(run) {
        synchronized(messages) {
          //STUCK HERE!
          try {messages.wait();}catch(InterruptedException e) {}
          //send messages
          ...
        }
    }
}

我使用this answer设计了代码,但这是错误的,或者我没有正确解释它。这是发生的事情:

  1. 线程readQueue启动。
  2. 线程readQueue阻止messages阻止synchronized阻止。
  3. 线程readQueuemessages.wait()上阻止自己。
  4. 另一个线程调用add("...")方法。
  5. 另一个帖子卡在synchronized块上。
  6. 永远无法调用messages.notifyAll()。当然,最初,在搜索之前,我试图这样做:

    //Runs from special thread
    private void readQueue() {
        Log.debug("Waiting for messages to send.");
        while(run) {
            //Wait before getting noticed of new message
            try {messages.wait();}catch(InterruptedException e) {}
            //Block messages, read them, clear them
            synchronized(messages) {
              //send messages
              ...
            }
        }
    }
    

    这会引发illegal monitor exception,这迫使我将wait放入synchronized - 我们就在我们开始的地方 - 卡住了。

2 个答案:

答案 0 :(得分:0)

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

防止你的邪恶&#34;例如,你需要一个保护while循环。如果通知了消费者线程,但是messages被清空,则会在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.

OR

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.

答案 1 :(得分:0)

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

我所要做的只是将发送消息的waitwhile放入不同的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(); 
    }
}
  1. readQueue主题进入synchronized阻止
  2. readQueue主题调用messages.wait()并发布messages
  3. 另一个帖子运行evil()
  4. 恶意通知并允许readQueue继续
  5. 此时,两个synchronized块正在messages
  6. 上运行

    我想知道检查是如何实施的以及允许哪些操作。我没有证据(例如我在文档中没有发现任何相关内容),但事实上第一个代码适用于我而第二个代码不适用于此。