[英]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:
这是发生的事情:
readQueue
starts.readQueue
启动。readQueue
blocks messages
by synchronized
block.readQueue
通过synchronized
块readQueue
阻塞messages
。readQueue
blocks itself on messages.wait()
.readQueue
在messages.wait()
上阻塞自己。add("...")
method.add("...")
方法。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.我所要做的就是把
wait
和while
将消息发送到不同的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();
}
}
readQueue
thread enters synchronized
block readQueue
线程进入synchronized
块readQueue
thread calls messages.wait()
and releases messages
readQueue
线程调用messages.wait()
并释放messages
evil()
evil()
readQueue
to continue readQueue
继续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.