[java] ExecutorService를 사용하여 모든 스레드가 완료되기를 기다리는 방법은 무엇입니까?
한 번에 몇 가지 작업 4를 다음과 같이 실행해야합니다.
ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
taskExecutor.execute(new MyTask());
}
//...wait for completion somehow
모두 완료되면 어떻게 알림을받을 수 있습니까? 지금은 전역 작업 카운터를 설정하는 것보다 더 나은 것을 생각할 수 없으며 모든 작업이 끝날 때마다 그것을 줄인 다음 무한 루프 에서이 카운터를 모니터링하여 0이되도록하십시오. 또는 선물 목록을 얻거나 무한 루프 모니터에서 isDone for all them. 무한 루프를 포함하지 않는 더 나은 솔루션은 무엇입니까?
감사.
답변
기본적으로 ExecutorService
당신은 전화 shutdown()
를 한 다음 awaitTermination()
:
ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
taskExecutor.execute(new MyTask());
}
taskExecutor.shutdown();
try {
taskExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
...
}
답변
CountDownLatch를 사용하십시오 .
CountDownLatch latch = new CountDownLatch(totalNumberOfTasks);
ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
taskExecutor.execute(new MyTask());
}
try {
latch.await();
} catch (InterruptedException E) {
// handle
}
그리고 당신의 작업 내에서 (시도 / 마지막으로 동봉하십시오)
latch.countDown();
답변
ExecutorService.invokeAll()
당신을 위해 그것을합니다.
ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
List<Callable<?>> tasks; // your tasks
// invokeAll() returns when all tasks are complete
List<Future<?>> futures = taskExecutor.invokeAll(tasks);
답변
선물리스트를 사용할 수도 있습니다.
List<Future> futures = new ArrayList<Future>();
// now add to it:
futures.add(executorInstance.submit(new Callable<Void>() {
public Void call() throws IOException {
// do something
return null;
}
}));
그런 다음 모든 멤버에 참여하려고 할 때 본질적으로 각 멤버에 참여하는 것과 같습니다 (자식 스레드에서 메인 스레드로 예외를 다시 발생시키는 이점이 있습니다).
for(Future f: this.futures) { f.get(); }
기본적으로 트릭은 (모든 또는 각각의) isDone ()을 무한 루프하는 대신 각 Future에서 .get ()을 한 번에 하나씩 호출하는 것입니다. 따라서 마지막 스레드가 완료 되 자마자이 블록을 통해 “이동”할 수 있습니다. 주의 사항은 .get () 호출이 예외를 다시 발생시키기 때문에 스레드 중 하나가 죽으면 다른 스레드가 완료되기 전에 가능한 것입니다. [이를 피하기 위해 catch ExecutionException
get 호출 주위에 추가 할 수 있습니다] ]. 다른주의 사항은 모든 스레드에 대한 참조를 유지하므로 스레드 로컬 변수가있는 경우이 블록을 통과 한 후에도 수집되지 않습니다 (문제가되면이 문제를 해결할 수는 있지만 제거하여 해결할 수 있음) 미래는 ArrayList에서 벗어남). 어떤 미래가 “먼저 끝나는가”를 알고 싶다면https://stackoverflow.com/a/31885029/32453
답변
Java8에서 당신은 그것을 할 수 CompletableFuture :
ExecutorService es = Executors.newFixedThreadPool(4);
List<Runnable> tasks = getTasks();
CompletableFuture<?>[] futures = tasks.stream()
.map(task -> CompletableFuture.runAsync(task, es))
.toArray(CompletableFuture[]::new);
CompletableFuture.allOf(futures).join();
es.shutdown();
답변
내 두 센트. CountDownLatch
미리 작업 수를 알아야 한다는 요구 사항을 극복하기 위해 간단한을 사용하여 구식 방식으로 할 수 Semaphore
있습니다.
ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
int numberOfTasks=0;
Semaphore s=new Semaphore(0);
while(...) {
taskExecutor.execute(new MyTask());
numberOfTasks++;
}
try {
s.aquire(numberOfTasks);
...
당신의 작업에서는 단지 전화 s.release()
당신이하는 것처럼latch.countDown();
답변
게임에 약간 늦었지만 완료를 위해 …
모든 작업이 완료되기를 ‘대기’하는 대신 할리우드 원칙에 따라 “나에게 전화하지 마십시오. 전화하겠습니다”라고 생각할 수 있습니다. 결과 코드가 더 우아하다고 생각합니다 …
구아바는이를 달성하기위한 몇 가지 흥미로운 도구를 제공합니다.
예 ::
ExecutorService를 ListeningExecutorService로 랩핑 ::
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
실행할 콜 러블 컬렉션 제출 ::
for (Callable<Integer> callable : callables) {
ListenableFuture<Integer> lf = service.submit(callable);
// listenableFutures is a collection
listenableFutures.add(lf)
});
이제 필수 부분 :
ListenableFuture<List<Integer>> lf = Futures.successfulAsList(listenableFutures);
모든 선물이 완료 될 때 알림을받을 수 있도록 ListenableFuture에 콜백을 연결합니다. ::
Futures.addCallback(lf, new FutureCallback<List<Integer>>() {
@Override
public void onSuccess(List<Integer> result) {
log.info("@@ finished processing {} elements", Iterables.size(result));
// do something with all the results
}
@Override
public void onFailure(Throwable t) {
log.info("@@ failed because of :: {}", t);
}
});
또한 처리가 완료되면 모든 결과를 한곳에서 수집 할 수 있다는 이점도 제공합니다.
더 자세한 정보는 여기