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();
}
}