1

プログラム内に複数のRunnableインスタンスがあり、すべてがExecutorインスタンスによってディスパッチされているとします。さらに、ある時点で、これらのランナブルのサブセットが終了するのを待ってから次に進む必要があるとします。

これを行う1つの方法は次のとおりです。

public abstract class Joinable implements Runnable {

    private final Semaphore finishedLock = new Semaphore(1);

    @Override
    public final void run() {
        try {
            finishedLock.acquireUninterruptibly();
            doWork();
        } finally {
            finishedLock.release();
        }
    }

    public abstract void doWork();

    public void join() {
        finishedLock.acquireUninterruptibly();
    }
}

クラスを実装すると、実行中に何を実行するかを定義するために、run()ではなくdoWork()を単純にオーバーライドできます。

参加プロセスは次のようになります。

void doStuff() {

    Executor executor = Executors.newCachedThreadPool();

    List<Joinable> joinables = new LinkedList<Joinable>();
    // Fill joinables with implementors of Joinable...

    List<Runnable> others = new LinkedList<Runnable>();
    // Fill others with implementors of Runnable...

    for(Joinable joinable : joinables) 
        executor.execute(joinable);

    for(Runnable runnable : others) 
        executor.execute(runnable);

    for(Joinable joinable : joinables) 
        joinable.join();

    // Continue, no matter what the threads in others are up to.
}

これはこの問題を解決するための良い方法ですか(それは安全ですか?)、またはより良い方法がありますか?

4

2 に答える 2

2

現在のソリューションはスレッドセーフではありません。に電話をかける前に、エグゼキュータがrunあなたに電話をかけるという保証はありません。したがって、場合によっては、メインスレッドがロックを取得する前に取得します。JoinablejoinJoinable

CountDownLatch考えられる解決策は、参加可能なものの総数がわかっている場合は、代わりにを使用してN、を作成し、CountDownLatch(N)それを各インスタンスに渡すことです。各参加可能ファイルが終了したら、を呼び出しますcountDown()。メインスレッドがawait()ラッチを呼び出します。 await()ラッチカウントが0になるまで戻りません。

于 2013-03-26T21:02:05.777 に答える
1

これはこの問題を解決するための良い方法ですか(安全ですか?)

これは完全に正しくありません。Runnableによって実行しているに参加することはできませんExecutorService。リストを使用する場合は、次のようにします。

List<Future<?>> futures = new ArrayList<Future<?>>();
for(Joinable joinable : joinables) {
   // this submit returns a `Future`.
   futures.add(executor.submit(joinable));
}
// submit others to the executor _without_ adding to the futures list

for (Future<?> future : futures) {
    // this can throw ExecutionException which wraps exception thrown by task
    future.get();
}

それとももっと良いものがありますか?

すべてのタスクが完了するのを待っている場合は、このメソッドを使用できますExecutorService.awaitTermination(long timeout, TimeUnit unit)。例えば:

executor.awaitTerminate(Long.MAX_VALUE, TimeUnit.MILLISECONDS);

しかし、タスクのサブセットを待っている場合、これを行うためのより良い方法はわかりません。

于 2013-03-26T20:57:30.307 に答える