因此,我有以下对象(为示例而简化):
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();
}
}
}
我在主线程中的某处有这段代码(再次简化):
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....
现在,EventGenerator对象在不同线程上使用val = 1调用“onDataAvailable”,然后使用val = 2调用。基本上,我希望看到的是:
listener received val: 1
value is: 1
listener received val: 2
但是,我通常会得到:
listener received val: 1
listener received val: 2
value is: 2
好像第二个“onDataAvailable”调用在唤醒主线程之前获得了锁。在同步的“onDataAvailable”块之后进行简单的println或短暂的 sleep 就足以获得预期的结果,但这似乎是一个丑陋的补丁。
我在这里做错了什么?
注意,我对调用监听器的线程没有控制权。它基本上是一个通过网络接收事件的线程。有时它将在同一条消息中接收到多个事件,因此会一次又一次地多次调用“onDataAvailable”,这导致了我的问题。有时,它将在两条不同的消息中接收到两个事件,这为主线程在事件之间唤醒留出了足够的时间。
最佳答案
如果您有多个调用onDataAvailable(...)
的线程,这是可以预期的。调用notifyAll()
时,所有在该对象上等待的线程都移到了阻塞的队列中,但在该队列中已有的所有线程之后。他们都必须等待lock
上的同步才能继续。
是的,因此多个网络处理程序线程正在调用onDataAvailable(...)
。第二个被阻止在synchronized(lock)
上等待它。调用notifyAll()
时,另一个线程也进入块队列,但在另一个处理程序之后。
如果只有一个处理程序线程,您会得到该输出,我会感到惊讶。在这种情况下,被通知的线程应该在单线程处理程序可以解锁,读取另一条消息并再次锁定之前获得同步锁。
问题不在于处理线程的方式,而在于您处理receivedVal
的方式。您应该立即在处理线程中处理该值,或者需要将其放入某种同步队列(可能是 LinkedBlockingQueue
)中,以便由主线程按顺序打印出来。
如果您使用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);
}