1

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

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 或短暂睡眠就足以获得预期的结果,但这似乎是一个丑陋的补丁。

我在这里做错了什么?

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

4

1 回答 1

6

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

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

其他时候,它会在两个不同的消息中接收两个事件,从而为主线程在事件之间唤醒足够的时间。

对,所以多个网络处理程序线程正在调用onDataAvailable(...). 第二个被阻塞在synchronized(lock)等待它。当notifyAll()被调用时,另一个线程也进入块队列,但另一个处理程序之后。

如果只有一个处理程序线程,您会得到该输出,我会感到惊讶。在这种情况下,被通知的线程应该在单线程处理程序可以解锁、读取另一条消息并再次锁定之前获得同步锁。

我在这里做错了什么?

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

如果您使用 a ,BlockingQueue那么主队列只​​会执行 a queue.take(),这会导致它等待结果,而处理程序线程只会执行queue.put(...). 您不需要自己进行wait()ornotifyAll()调用。

像这样的东西会起作用:

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);
}
于 2012-09-05T20:57:28.723 回答