简体   繁体   English

为消息队列创建一个阻塞线程

[英]Make a blocking thread for message queue

I'm making a simple server-client application.我正在制作一个简单的服务器-客户端应用程序。 I'm handling the message queue this way ( class MessageQueue ):我正在以这种方式处理消息队列( 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
          ...
        }
    }
}

I designed the code using this answer , but it's wrong or I haven't interpreted it correctly.我使用这个答案设计了代码,但它是错误的或者我没有正确解释它。 Here's what happens:这是发生的事情:

  1. Thread readQueue starts.线程readQueue启动。
  2. Thread readQueue blocks messages by synchronized block.线程readQueue通过synchronizedreadQueue阻塞messages
  3. Thread readQueue blocks itself on messages.wait() .线程readQueuemessages.wait()上阻塞自己。
  4. Another thread calls add("...") method.另一个线程调用add("...")方法。
  5. Another thread gets stuck on synchronized block.另一个线程卡在synchronized块上。

The messages.notifyAll() can never be called. messages.notifyAll()永远不能被调用。 Of course, originally, before searching, I was trying to do this:当然,最初,在搜索之前,我试图这样做:

//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
          ...
        }
    }
}

This throws illegal monitor exception , which forces me to put wait into synchronized - and we're just where we begun - stuck.这会引发非法监视器异常,这迫使我将wait置于synchronized - 而我们只是开始的地方 - 卡住了。

A thread can't be stuck on the add method, since messages.wait() releases the object monitor.线程不能停留在add方法上,因为messages.wait()释放了对象监视器。 That way when your special thread is wait() ing, other threads are free to enter the synchronized block in add() (but one at a time only).这样,当您的特殊线程是wait() ing 时,其他线程可以自由地进入add()的同步块(但一次只能进入一个)。

To prevent your "evil" example, you need a guarding while loop.为了防止您的“邪恶”示例,您需要一个保护 while 循环。 If the consumer thread is notified, but messages is emptied, it will notice that in the while loop and wait() again.如果消费者线程被通知,但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();
    }
}

Edit: The timeline is as follows, Thread1 is producer, Thread2 is consumer.编辑:时间线如下,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.

So, while noone has given me a solution I've been testing and thinking.所以,虽然没有人给我一个解决方案,但我一直在测试和思考。 As Kayaman has pointed out, wait call releases the variable in current synchronized block.正如Kayaman所指出的, wait调用释放当前synchronized块中的变量。 But there is more to it.但还有更多。
Java is smart enough to prevent conflicts and will not release the synchronised of other operations are performed on the variable in the block. Java 足够聪明,可以防止冲突,并且不会释放对块中变量执行的其他操作的synchronised

All I had to do was putting the wait and the while that sends messages into different synchronized blocks.我所要做的就是把waitwhile将消息发送到不同的synchronized块。
This is correct code that works:这是有效的正确代码

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
        }
    }
}

It looks a little silly, but it makes sense.看起来有点傻,但很有道理。 Consider the old, wrong code and a scenario:考虑旧的错误代码和场景:

//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 thread enters synchronized block readQueue线程进入synchronized
  2. readQueue thread calls messages.wait() and releases messages readQueue线程调用messages.wait()并释放messages
  3. Another thread runs evil()另一个线程运行evil()
  4. Evil calls notify and allows readQueue to continue Evil 调用 notify 并允许readQueue继续
  5. At this moment, two synchronized blocks are operating on messages此时,两个synchronized块正在对messages进行操作

I wonder how exactly is the checking implemented and what actions are allowed.我想知道检查究竟是如何实施的,以及允许采取哪些行动。 I have no proof (eg I have found nothing about this in docs) for this but the fact that the first code works for me and the second does not.我没有证据(例如,我在文档中没有找到任何关于此的证据),但事实是第一个代码对我有用,而第二个则没有。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM