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」の2番目の呼び出しが、メインスレッドが起動する前にロックを取得するかのようです。期待される結果を得るには、単純なprintlnまたは「onDataAvailable」の同期ブロック後の短いスリープで十分ですが、それは醜いパッチのようです。

私はここで何が間違っているのですか?

リスナーを呼び出すスレッドを制御できないことに注意してください。これは基本的に、ネットワークを介してイベントを受信するスレッドです。同じメッセージで複数のイベントを受信することがあるため、「onDataAvailable」を次々に複数回呼び出すことがあり、これが問題につながります。また、2つの異なるメッセージで2つのイベントを受信する場合もあります。これにより、メインスレッドがイベント間でウェイクアップするのに十分な時間が残ります。

4

1 に答える 1

6

これは、「onDataAvailable」の2番目の呼び出しが、メインスレッドが起動する前にロックを取得するかのようです。

これは、を呼び出す複数のスレッドがある場合に予想されますonDataAvailable(...)。がnotifyAll()呼び出されると、そのオブジェクトを待機しているすべてのスレッドがブロックされたキューに移動されますが、すでにキューにあるスレッドの背後にあります。それらはすべて、lock続行する前に同期するのを待つ必要があります。

また、2つの異なるメッセージで2つのイベントを受信する場合もあります。これにより、メインスレッドがイベント間でウェイクアップするのに十分な時間が残ります。

そうです、複数のネットワークハンドラスレッドがを呼び出してonDataAvailable(...)います。synchronized(lock)2番目のものはそれを待っているときにブロックされます。がnotifyAll()呼び出されると、他のスレッドもブロックキューに入りますが、他のハンドラーの背後にあります。

ハンドラースレッドが1つしかない場合に、その出力が得られることに驚かれることでしょう。その場合、通知されたスレッドは、シングルスレッドハンドラーがロックを解除して別のメッセージを読み取り、再度ロックする前に、同期ロックを取得する必要があります。

私はここで何が間違っているのですか?

問題は、スレッドの処理方法ではなく、の処理方法にありますreceivedVal。値を処理スレッドですぐLinkedBlockingQueueに処理する必要があります。そうしないと、メインスレッドによって順番に出力されるように、値をある種の同期キュー(おそらく)に入れる必要があります。

を使用するBlockingQueue場合、メインキューはaqueue.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);
}
于 2012-09-05T20:57:28.723 に答える