Если вы управляете созданием потоков (отправка в службу ExecutorService), тогда вы можете использовать ExecutorCompletionService
см. ExecutorCompletionService? Зачем нужен один, если у нас есть invokeAll? для различных ответов там.
Если вы не управляете созданием потока, вот подход, который позволяет вам присоединяться к потокам «один за другим по мере их завершения» (и знать, какой из них заканчивается первым и т. Д.), Вдохновленный рубином ThreadWait класс.
По сути, обновляя «наблюдающие потоки», которые предупреждают о завершении других потоков, вы можете узнать, когда «следующий» поток из многих завершается.
Вы бы использовали что-то вроде этого:
JoinThreads join = new JoinThreads(threads);
for(int i = 0; i < threads.size(); i++) {
Thread justJoined = join.joinNextThread();
System.out.println("Done with a thread, just joined=" + justJoined);
}
И источник:
public static class JoinThreads {
java.util.concurrent.LinkedBlockingQueue<Thread> doneThreads =
new LinkedBlockingQueue<Thread>();
public JoinThreads(List<Thread> threads) {
for(Thread t : threads) {
final Thread joinThis = t;
new Thread(new Runnable() {
@Override
public void run() {
try {
joinThis.join();
doneThreads.add(joinThis);
}
catch (InterruptedException e) {
// "should" never get here, since we control this thread and don't call interrupt on it
}
}
}).start();
}
}
Thread joinNextThread() throws InterruptedException {
return doneThreads.take();
}
}
Приятной частью этого является то, что он работает с общими потоками Java, без изменений, любой поток может быть присоединен. Предостережение заключается в том, что требуется создание дополнительных потоков. Также эта конкретная реализация «оставляет потоки позади», если вы не вызываете joinNextThread () полное количество раз, и у вас нет метода «close» и т. Д. Прокомментируйте здесь, если вы хотите создать более совершенную версию. Вы также можете использовать этот же тип паттерна с "Futures" вместо объектов Thread и т. Д.