0

2つのブロッキングキューがある状況があります。最初に、実行するタスクをいくつか挿入します。各タスクが完了すると、タスクが2番目のキューに追加され、そこで実行されます。

したがって、最初のキューは簡単です。空でないことを確認して実行するだけです。それ以外の場合は、interrupt()を実行します。

public void run() {
    try {
        if (taskQueue1.isEmpty()) {
            SomeTask task = taskQueue1.poll();
            doTask(task);
            taskQueue2.add(task);
        }
        else {
            Thread.currentThread().interrupt();
        }
    }

    catch (InterruptedException ex) {
        ex.printStackTrace();
    }
}

あなたが言うことができるように、私が次のことをする2番目のものは機能しません:

public void run() {
    try {
        SomeTask2 task2 = taskQueue2.take();
        doTask(task2);
    }

    catch (InterruptedException ex) {

    }
    Thread.currentThread().interrupt();

}

2番目のBlockingQueueがtake()でブロックせず、追加するアイテムがこれ以上ないことがわかった場合にのみ終了するように、どのように解決しますか。2番目のスレッドがおそらく1番目のブロッキングキューを認識し、それが空であり、2番目のキューも空であるかどうかを確認すると、割り込みが発生する可能性があります。

Poisonオブジェクトを使用することもできますが、他のものを使用したいと思います。

注意:これは正確なコードではなく、私がここに書いたものです。

4

2 に答える 2

2

ここで実際に何をしようとしているのかわかりませんがinterrupt()、最初のrun()方法は無意味または間違っていると言えます。

  • run()独自のオブジェクトでメソッドを実行している場合Thread、そのスレッドはとにかく終了しようとしているため、中断する意味がありません。

  • スレッドプールを使用してエグゼキューターでメソッドを実行している場合run()、おそらくスレッドを強制終了したり、エグゼキューターをシャットダウンしたりしたくないでしょう...その時点で。また、エグゼキューターをシャットダウンしたい場合は、そのシャットダウン メソッドの 1 つを呼び出す必要があります。


たとえば、これは、すべての割り込みがなく、スレッドの作成/破壊チャーンがなくても、あなたがしているように見えることを行うバージョンです。

public class TaskExecutor {

    private ExecutorService executor = new ThreadPoolExecutorService(...);

    public void submitTask1(final SomeTask task) {
        executor.submit(new Runnable(){
            public void run() {
                doTask(task);
                submitTask2(task);
            }
        });
    }

    public void submitTask2(final SomeTask task) {
        executor.submit(new Runnable(){
            public void run() {
                doTask2(task);
            }
        });
    }

    public void shutdown() {
        executor.shutdown();
    }
 }

タスクを個別にキューイングする場合は、2 つの異なるエグゼキュータを作成して使用するだけです。

于 2012-01-04T01:23:28.747 に答える
1

最初のキューを処理するスレッドが、キューが空になるとすぐにタスクが来ないことを知っているかのように聞こえます。怪しいと思われるかもしれませんが、あなたの言葉を信じて解決策を提案します。

AtomicInteger両方のスレッドに表示される を定義します。正の値に初期化します。

最初のスレッドの操作を次のように定義します。

  • ループしQueue#poll()ます。
  • Queue#poll()null を返す場合はAtomicInteger#decrementAndGet()、共有整数を呼び出します。
    • ゼロが返された場合AtomicInteger#decrementAndGet()は、 を介して 2 番目のスレッドを中断しますThread#interrupt()。(これは、アイテムが到着しなかった場合を処理します。)
    • どちらの場合も、ループを終了します。
  • それ以外の場合は、抽出されたアイテムを処理し、共有整数を呼び出しAtomicInteger#incrementAndGet()、抽出されたアイテムを 2 番目のスレッドのキューに追加して、ループを続行します。

2 番目のスレッドの操作を次のように定義します。

  • のループ ブロッキングBlockingQueue#take()
  • BlockingQueue#take()throwsの場合InterruptedException、例外をキャッチし、 を呼び出しThread.currentThread().interrupt()て、ループを終了します。
  • それ以外の場合は、抽出されたアイテムを処理します。
  • AtomicInteger#decrementAndGet()共有整数を 呼び出します。
    • ゼロが返された場合AtomicInteger#decrementAndGet()は、ループを終了します。
    • それ以外の場合は、ループを続行します。

実際のコードを書く前に、その考え方を理解していることを確認してください。契約では、予想されるタスクの数がゼロになるまで、2 番目のスレッドがそのキューからさらに多くのアイテムを待ち続けます。その時点で、生成スレッド (最初のスレッド) は新しいアイテムを 2 番目のスレッドのキューにプッシュしなくなるため、2 番目のスレッドはそのキューのサービスを安全に停止できることを認識します。

最初のスレッドのキューにタスクがまったく到着しない場合、厄介なケースが発生します。2 番目のスレッドは、アイテムを処理した後にカウントをデクリメントしてテストするだけなので、アイテムを処理する機会がまったくない場合、停止を検討することはありません。最初のスレッドのループ終了ステップで別の条件付き分岐を犠牲にして、スレッド割り込みを使用してそのケースを処理します。幸いなことに、そのブランチは 1 回だけ実行されます。

ここで機能する多くのデザインがあります。追加エンティティー (共有原子整数) を 1 つだけ導入したものについて説明しただけですが、それでも面倒です。ポイズン ピルを使用すると、はるかにクリーンになると思いますが、null を有効な要素として受け入れることQueue#add()も受け入れないことも認めます (の戻り値の契約のため)。そうでなければ、null をポイズン ピルとして使用するのは簡単です。BlockingQueue#put()Queue#poll()

于 2012-01-04T01:42:13.603 に答える