[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 ExecutionExceptionget 호출 주위에 추가 할 수 있습니다] ]. 다른주의 사항은 모든 스레드에 대한 참조를 유지하므로 스레드 로컬 변수가있는 경우이 블록을 통과 한 후에도 수집되지 않습니다 (문제가되면이 문제를 해결할 수는 있지만 제거하여 해결할 수 있음) 미래는 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);
        }
    });

또한 처리가 완료되면 모든 결과를 한곳에서 수집 할 수 있다는 이점도 제공합니다.

더 자세한 정보는 여기