我正在制作一个简单的服务器 - 客户端应用程序。我正在以这种方式处理消息队列(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设计了代码,但这是错误的,或者我没有正确解释它。这是发生的事情:
readQueue
启动。readQueue
阻止messages
阻止synchronized
阻止。readQueue
在messages.wait()
上阻止自己。add("...")
方法。synchronized
块上。永远无法调用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
- 我们就在我们开始的地方 - 卡住了。
答案 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
对块中的变量执行的其他操作。
我所要做的只是将发送消息的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
继续synchronized
块正在messages
我想知道检查是如何实施的以及允许哪些操作。我没有证据(例如我在文档中没有发现任何相关内容),但事实上第一个代码适用于我而第二个代码不适用于此。