简体   繁体   English

Java notifyAll不会立即唤醒

[英]Java notifyAll not waking right away

So, I have the following object (simplified for sake of example): 因此,我有以下对象(为示例而简化):

public class SomeListener implements EventListener{
    public final Object lock = new Object();
    public int receivedVal;

    @Override
    public onDataAvailable(int val){
        synchronized(lock){
            System.out.println("listener received val: " + val);
            receivedVal = val;
            lock.notifyAll();
        }
    }
}

And I have this piece of code somewhere in the main thread (again, simplified): 我在主线程中的某处有这段代码(再次简化):

SomeListener listener = new SomeListener();
EventGenerator generatorThread = new EventGenerator();
generatorThread.addListener(listener);
synchronize(listener.lock){
    generatorThread.start();
    listener.lock.wait();
    System.out.println("value is: " + listener.receivedVal);
}
//some other stuff here....

Now, the EventGenerator object calls "onDataAvailable" with val = 1, then with val = 2 on a different thread. 现在,EventGenerator对象在另一个线程上使用val = 1调用“ onDataAvailable”,然后使用val = 2调用。 Basically, what I expect to see is: 基本上,我希望看到的是:

listener received val: 1
value is: 1
listener received val: 2

However, I usually get: 但是,我通常会得到:

listener received val: 1
listener received val: 2
value is: 2

It is as if the second call of "onDataAvailable" acquires the lock before the main thread is awaken. 好像第二次调用“ onDataAvailable”在唤醒主线程之前获得了锁。 A simple println or a short sleep after the synchronized block of "onDataAvailable" is enough to get the expected result, but that seems like an ugly patch. 在同步的“ onDataAvailable”块之后进行简单的println或短暂的睡眠就足以获得预期的结果,但这似乎是一个丑陋的补丁。

What am I doing wrong here? 我在这里做错了什么?

Note, I do not have control on the thread that calls the listener. 注意,我对调用侦听器的线程没有控制权。 It's basically a thread that receives events over the network. 它基本上是一个通过网络接收事件的线程。 Sometimes it will receive multiple events in the same message and will therefore call "onDataAvailable" multiple times one after the other, which leads to my problem. 有时它将在同一条消息中接收到多个事件,因此将一次又一次地多次调用“ onDataAvailable”,这导致了我的问题。 Other times it will receive two events in two different messages, which leaves enough time for the main thread to awake between the events. 有时,它将在两条不同的消息中接收到两个事件,这为主线程在事件之间唤醒留出了足够的时间。

It is as if the second call of "onDataAvailable" acquires the lock before the main thread is awaken 好像第二次调用“ onDataAvailable”在唤醒主线程之前获得了锁

This is to be expected if you have multiple threads calling onDataAvailable(...) . 如果您有多个线程调用onDataAvailable(...)这是可以预期的。 When notifyAll() is called, all of the threads that are waiting on that object are moved to the blocked queue but behind any threads already in the queue. 调用notifyAll() ,所有在该对象上等待的线程都将移到阻塞队列中,但队列中已有线程之后。 They all have to wait to synchronize on the lock before continuing. 他们都必须等待lock同步才能继续。

Other times it will receive two events in two different messages, which leaves enough time for the main thread to awake between the events. 有时,它将在两条不同的消息中接收到两个事件,这为主线程在事件之间唤醒留出了足够的时间。

Right, so multiple network handler threads are calling onDataAvailable(...) . 是的,因此多个网络处理程序线程正在调用onDataAvailable(...) The 2nd one is blocked on the synchronized(lock) waiting for it. 第二个被阻塞在synchronized(lock)等待。 When notifyAll() is called, the other thread goes into the block queue as well but behind the other handler. 调用notifyAll() ,另一个线程也进入块队列,但另一个处理程序之后。

I would be surprised that you are getting that output if there is only one handler thread. 如果只有一个处理程序线程,您会得到该输出,我感到惊讶。 In that case, the notified thread should get the synchronize lock before the single thread handler can unlock, read another message, and lock again. 在这种情况下,被通知的线程应该单线程处理程序可以解锁,读取另一条消息并再次锁定之前获得同步锁。

What am I doing wrong here? 我在这里做错了什么?

The problem is not with the way the threads are being handled but by the way you are handling the receivedVal . 这个问题是不是与线程的处理方式,而是由你处理的方式receivedVal You should process the value immediately in the handling thread or you will need to put it in some sort of synchronized queue (maybe a LinkedBlockingQueue ) to be printed out by the main thread in order. 您应该立即在处理线程中处理该值,或者需要将其放入某种同步队列(可能是LinkedBlockingQueue )中,以便由主线程按顺序打印出来。

If you use a BlockingQueue then the main queue just does a queue.take() which causes it to wait for a result and the handler threads just do a queue.put(...) . 如果您使用BlockingQueue则主队列仅执行queue.take() ,这导致它等待结果,而处理程序线程仅执行queue.put(...) You would not need to do the wait() or notifyAll() calls yourself. 您不需要自己进行wait()notifyAll()调用。

Something like this would work: 这样的事情会起作用:

private final BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();
...

@Override
public onDataAvailable(int val){
    System.out.println("listener received val: " + val);
    queue.put(val);
}
...

generatorThread.addListener(listener);
generatorThread.start();
while (true) {
    // this waits for the queue to get a value
    int val = queue.take();
    System.out.println("value is: " + val);
}

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

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