Java notifyAll不会马上醒来

Phi*_*Bel 1 java multithreading

所以,我有以下对象(为了举例简化):

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我在主线程的某处(再次,简化)中有这段代码:

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....
Run Code Online (Sandbox Code Playgroud)

现在,EventGenerator对象使用val = 1调用"onDataAvailable",然后在另一个线程上调用val = 2.基本上,我期望看到的是:

listener received val: 1
value is: 1
listener received val: 2
Run Code Online (Sandbox Code Playgroud)

但是,我通常得到:

listener received val: 1
listener received val: 2
value is: 2
Run Code Online (Sandbox Code Playgroud)

就好像第二次调用"onDataAvailable"在主线程被唤醒之前获取了锁.在"onDataAvailable"的同步块之后的简单println或短暂睡眠足以获得预期的结果,但这似乎是一个丑陋的补丁.

我在这做错了什么?

注意,我无法控制调用侦听器的线程.它基本上是一个通过网络接收事件的线程.有时它会在同一条消息中收到多个事件,因此会一个接一个地多次调用"onDataAvailable",这会导致我的问题.其他时候它将在两个不同的消息中接收两个事件,这留下足够的时间让主线程在事件之间唤醒.

Gra*_*ray 6

就好像第二次调用"onDataAvailable"在主线程被唤醒之前获取了锁

如果你有多个线程调用,这是预期的onDataAvailable(...).当notifyAll()被调用时,所有正在等待该对象的线程移动到阻塞队列中,但后面已经在队列中的任何线程.lock在继续之前,他们都必须等待同步.

其他时候它将在两个不同的消息中接收两个事件,这留下足够的时间让主线程在事件之间唤醒.

是的,所以多个网络处理程序线程正在调用onDataAvailable(...).第二个在synchronized(lock)等待它时被阻止.当notifyAll()被调用时,其他线程进入队列块也不过落后其它的处理器.

如果只有一个处理程序线程,我惊讶于你得到了那个输出.在这种情况下,通知的线程应该单线程处理程序解锁,读取另一条消息并再次锁定之前获得同步锁定.

我在这做错了什么?

问题不在于处理线程的方式,而在于处理线程的方式receivedVal.您应该在处理线程中立即处理该值,或者您需要将其放入某种同步队列(可能是a LinkedBlockingQueue)中,以便主线程按顺序打印出来.

如果你使用a BlockingQueue那么主队列只​​是做一个queue.take()导致它等待结果的处理程序线程只做一个queue.put(...).你不需要自己做wait()notifyAll()打电话.

像这样的东西会起作用:

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);
}
Run Code Online (Sandbox Code Playgroud)