¿Cómo esperar a que todos los hilos terminen, utilizando ExecutorService?

381

Necesito ejecutar una cantidad de tareas 4 a la vez, algo como esto:

ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
    taskExecutor.execute(new MyTask());
}
//...wait for completion somehow

¿Cómo puedo recibir una notificación una vez que todos estén completos? Por ahora no puedo pensar en nada mejor que configurar un contador de tareas global y disminuirlo al final de cada tarea, luego monitorear en bucle infinito este contador para convertirlo en 0; u obtener una lista de Futuros y en el monitor de bucle infinito está Hecho para todos ¿Cuáles son las mejores soluciones que no involucran bucles infinitos?

Gracias.

serg
fuente

Respuestas:

446

Básicamente en una ExecutorServicellamada shutdown()y luego awaitTermination():

ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
  taskExecutor.execute(new MyTask());
}
taskExecutor.shutdown();
try {
  taskExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
  ...
}
cletus
fuente
99
esto es exactamente para lo que está destinado el apagado / espera Terminación
matt b
31
Es un buen patrón si este manejo de tareas es un evento de una sola vez. Sin embargo, si esto se hace repetidamente durante el mismo tiempo de ejecución, no es óptimo, ya que crearía y derribaría hilos repetidamente cada vez que se ejecute.
sjlee
44
Estoy buscando cualquier documentación oficial que Long.MAX_VALUE, TimeUnit.NANOSECONDSsea ​​equivalente a no tener un tiempo de espera.
Sam Harwell
15
No puedo creer que tenga que usar el apagado para unirse a todos los hilos actuales (después de usar el apagado, no puede usar el ejecutor nunca más). Sugerir el uso de la lista de lugar ... del Futuro
rogerdpack
20
@SamHarwell vea la documentación del java.util.concurrentpaquete en la sección: Para esperar "para siempre", puede usar un valor deTimingLong.MAX_VALUE
beluchin
174

Use un CountDownLatch :

CountDownLatch latch = new CountDownLatch(totalNumberOfTasks);
ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
  taskExecutor.execute(new MyTask());
}

try {
  latch.await();
} catch (InterruptedException E) {
   // handle
}

y dentro de su tarea (adjunte en try / finally)

latch.countDown();
ChssPly76
fuente
3
No hay 4 tareas. Hay "una cierta cantidad de tareas" realizadas 4 a la vez.
cletus
2
Lo siento, entendí mal la pregunta. Sí, el número de tareas debería ser el argumento para el constructor
CountDownLatch
3
Encuentro esta solución más elegante que las otras, parece que fue hecha para este propósito, y es simple y directa.
wvdschel
3
¿Qué pasa si no sabe la cantidad de tareas antes de comenzar?
cletus
11
@cletus - entonces no usas un CountDownLatch :-) Eso sí, no estoy argumentando que este enfoque es mejor que el tuyo. Sin embargo, he encontrado que en los escenarios de la vida real yo no sé el número de tareas, la configuración de grupo de subprocesos no tienen que ser configurable por el despliegue, y las piscinas pueden ser reutilizados. Por lo general, Spring me inyecta grupos de hilos y los configuro como prototipos y los apago manualmente solo para esperar a que los hilos terminen parece menos que ideal.
ChssPly76
82

ExecutorService.invokeAll() lo hace por ti

ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
List<Callable<?>> tasks; // your tasks
// invokeAll() returns when all tasks are complete
List<Future<?>> futures = taskExecutor.invokeAll(tasks);
sjlee
fuente
La dificultad viene si / cuando tienes el inicio de los "4" hilos uno por uno, por partes, luego únete / deja terminar los 4 ...
rogerdpack
@rogerdpack: Todavía estoy aprendiendo estos ejecutores y esas cosas. Pero en respuesta a lo que preguntas. ¿Deberían los 4 hilos a la vez no ser parte de una tarea por lotes que se ejecuta utilizando la respuesta anterior?
Mukul Goel
3
Este método solo funcionará si conoce el número de tareas de antemano.
Konstantin
3
Creo que cuando futuresse devuelven, las tareas no se han completado. Pueden completarse en el futuro y tendrá un enlace al resultado. Por eso se llama a Future. Tiene el método Future.get () , que esperará a que finalice la tarea para obtener un resultado.
AlikElzin-kilaka
55
@ AlikElzin-kilaka Cita de los JavaDocs (vinculado en la respuesta): "Ejecuta las tareas dadas, devolviendo una lista de Futuros con su estado y resultados cuando se completan. Future.isDone () es verdadero para cada elemento de la lista devuelta. "
Hulk
47

También puedes usar Listas de Futuros:

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;
  }
}));

luego, cuando desee unirse a todos ellos, es esencialmente el equivalente de unirse a cada uno (con el beneficio adicional de que vuelve a generar excepciones de subprocesos secundarios a los principales):

for(Future f: this.futures) { f.get(); }

Básicamente, el truco es llamar a .get () en cada Futuro uno a la vez, en lugar de llamadas de bucle infinito isDone () en (todos o cada uno). Por lo tanto, tiene la garantía de "seguir adelante" a través de este bloque tan pronto como finalice el último hilo. La advertencia es que, dado que la llamada .get () vuelve a generar excepciones, si uno de los subprocesos muere, usted podría subir de esto posiblemente antes de que los otros subprocesos hayan finalizado [para evitar esto, puede agregar un mensaje catch ExecutionExceptionalrededor de la llamada get ] La otra advertencia es que mantiene una referencia a todos los subprocesos, por lo que si tienen variables locales de subprocesos, no se recopilarán hasta después de pasar este bloque (aunque es posible que pueda evitar esto, si se convirtió en un problema, eliminando El futuro está fuera de la ArrayList). Si quisieras saber qué futuro "termina primero"https://stackoverflow.com/a/31885029/32453

rogerdpack
fuente
3
Para saber qué "termina primero", use ExecutorCompletionService.take: stackoverflow.com/a/11872604/199364
ToolmakerSteve
34

En Java8 puedes hacerlo con 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();
AdamSkywalker
fuente
3
Esta es una solución muy elegante.
mattvonb
ExecutorService es = Executors.newFixedThreadPool(4); List< Future<?>> futures = new ArrayList<>(); for(Runnable task : taskList) { futures.add(es.submit(task)); } for(Future<?> future : futures) { try { future.get(); }catch(Exception e){ // do logging and nothing else } }
user2862544
@AdamSkywalker es awaitTermination () necesario después de es.shutdown ()?
gaurav
@gaurav cuando llamas al cierre, algunas tareas pueden no estar terminadas todavía. Entonces awaitTermination bloqueará el hilo de llamada hasta que todo esté hecho. Depende de si necesita esperar los resultados en este hilo o no.
AdamSkywalker
@AdamSkywalker gran respuesta. tiene sentido no llamar a awaitTermination () si no necesito esperar los resultados.
gaurav
26

Solo mis dos centavos. Para superar el requisito de CountDownLatchconocer el número de tareas de antemano, puede hacerlo a la antigua usanza de una manera simple Semaphore.

ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
int numberOfTasks=0;
Semaphore s=new Semaphore(0);
while(...) {
    taskExecutor.execute(new MyTask());
    numberOfTasks++;
}

try {
    s.aquire(numberOfTasks);
...

En tu tarea solo llama s.release()como lo haríaslatch.countDown();

Stryba
fuente
Al ver esto, primero me pregunté si sería un problema si se realizaran algunas releasellamadas antes de la acquirellamada, pero después de leer la documentación de Semaphore, veo que está bien.
ToolmakerSteve
13

Un poco tarde para el juego, pero para completarlo ...

En lugar de 'esperar' a que finalicen todas las tareas, puede pensar en términos del principio de Hollywood: "no me llame, lo llamaré", cuando haya terminado. Creo que el código resultante es más elegante ...

La guayaba ofrece algunas herramientas interesantes para lograr esto.

Un ejemplo ::

Envuelva un ExecutorService en ListeningExecutorService ::

ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));

Enviar una colección de invocables para su ejecución ::

for (Callable<Integer> callable : callables) {
  ListenableFuture<Integer> lf = service.submit(callable);
  // listenableFutures is a collection
  listenableFutures.add(lf)
});

Ahora la parte esencial:

ListenableFuture<List<Integer>> lf = Futures.successfulAsList(listenableFutures);

Adjunte una devolución de llamada al ListenableFuture, que puede usar para recibir notificaciones cuando se completen todos los futuros ::

        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);
        }
    });

Esto también ofrece la ventaja de que puede recopilar todos los resultados en un solo lugar una vez que finalice el procesamiento ...

Más información aquí.

Răzvan Petruescu
fuente
2
Muy limpio. Funciona perfectamente incluso en Android. Sólo tenía que usar runOnUiThread()en onSuccess().
DSchmidt
12

La clase CyclicBarrier en Java 5 y posterior está diseñada para este tipo de cosas.

Pekka Enberg
fuente
77
Genial, nunca puedo recordar el nombre de esta estructura de datos. Sin embargo, solo es adecuado si conoce de antemano la cantidad de tareas que se pondrán en cola.
ᆼ ᆺ ᆼ
Sí se podría pensar que sería capaz de golpear la barrera con el hilo actual, y todos los procesos hijo, entonces cuando usted pasó lo sabrías los procesos hijo se realizaron ...
rogerdpack
En realidad es una respuesta incorrecta. CyclicBarrier diseñado para porciones. CountDownLatch diseñado para eventos de espera
gstackoverflow
7

Siga uno de los siguientes enfoques.

  1. Iterar a través de todas las tareas futuras , regresar de submitencendido ExecutorServicey verificar el estado bloqueando la llamada get()en el Futureobjeto como lo sugiereKiran
  2. Usar invokeAll()en ExecutorService
  3. CountDownLatch
  4. ForkJoinPool o Executors.html # newWorkStealingPool
  5. Utilice las shutdown, awaitTermination, shutdownNowAPI de ThreadPoolExecutor en la secuencia adecuada

Preguntas SE relacionadas:

¿Cómo se usa CountDownLatch en Java Multithreading?

Cómo cerrar correctamente Java ExecutorService

Ravindra babu
fuente
6

Aquí hay dos opciones, solo confunda cuál es la mejor opción.

Opción 1:

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();

Opcion 2:

ExecutorService es = Executors.newFixedThreadPool(4);
List< Future<?>> futures = new ArrayList<>();
for(Runnable task : taskList) {
    futures.add(es.submit(task));
}

for(Future<?> future : futures) {
    try {
        future.get();
    }catch(Exception e){
        // do logging and nothing else
    }
}
es.shutdown();

Aquí poniendo future.get (); en el intento de captura es buena idea ¿verdad?

usuario2862544
fuente
5

Podrías envolver tus tareas en otro ejecutable, que enviará notificaciones:

taskExecutor.execute(new Runnable() {
  public void run() {
    taskStartedNotification();
    new MyTask().run();
    taskFinishedNotification();
  }
});
Zed
fuente
1
Me llevó un tiempo ver cómo esto resolvería la pregunta de OP. Primero, tenga en cuenta que este ajuste es de cada tarea, no del código que inicia todas las tareas. Presumiblemente, cada inicio incrementaría un contador, y cada final disminuiría ese contador, o incrementaría un completedcontador. Entonces, después de iniciarlos todos, en cada notificación, podría determinar si todas las tareas se han completado. Tenga en cuenta que es vital utilizarlo try/finallypara que se envíe una notificación finalizada (o una notificación alternativa en catchbloque) incluso si una tarea falla. De lo contrario, esperaría para siempre.
ToolmakerSteve
3

Acabo de escribir un programa de muestra que resuelve su problema. No se dio una implementación concisa, así que agregaré una. Si bien puede usar executor.shutdown()y executor.awaitTermination(), no es la mejor práctica, ya que el tiempo que tardan los diferentes subprocesos sería impredecible.

ExecutorService es = Executors.newCachedThreadPool();
    List<Callable<Integer>> tasks = new ArrayList<>();

    for (int j = 1; j <= 10; j++) {
        tasks.add(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                int sum = 0;
                System.out.println("Starting Thread "
                        + Thread.currentThread().getId());

                for (int i = 0; i < 1000000; i++) {
                    sum += i;
                }

                System.out.println("Stopping Thread "
                        + Thread.currentThread().getId());
                return sum;
            }

        });
    }

    try {
        List<Future<Integer>> futures = es.invokeAll(tasks);
        int flag = 0;

        for (Future<Integer> f : futures) {
            Integer res = f.get();
            System.out.println("Sum: " + res);
            if (!f.isDone()) 
                flag = 1;
        }

        if (flag == 0)
            System.out.println("SUCCESS");
        else
            System.out.println("FAILED");

    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
Kiran
fuente
Es bueno que muestres el uso de future.get: una buena alternativa para conocer. Pero, ¿por qué considera que es mejor esperar para siempre , que establecer un tiempo de espera máximo aceptable? Más importante aún, no hay razón para hacer toda esta lógica, cuando uno simplemente puede dar un tiempo muy, muy largo para esperar Terminación, si desea esperar (esencialmente para siempre) hasta que se completen todas las tareas.
ToolmakerSteve
Esto no es diferente de las soluciones ya presentadas aquí. Su solución justa es la misma presentada por @sjlee
pulp_fiction
No estoy seguro de por qué necesita verificar si está hecho cuando según el documento de Oracle, invokeAll solo devolverá "cuando todo esté completo o el tiempo de espera expire, lo que ocurra primero"
Mashrur
3

Solo para proporcionar más alternativas aquí diferentes para usar pestillos / barreras. También puede obtener los resultados parciales hasta que todos terminen con CompletionService .

Desde Java Concurrency en la práctica: "Si tiene que enviar un lote de cálculos a un Ejecutor y desea recuperar sus resultados a medida que estén disponibles, puede retener el Futuro asociado con cada tarea y sondear repetidamente su finalización llamando a get with a tiempo de espera cero. Esto es posible, pero tedioso . Afortunadamente hay una mejor manera : un servicio de finalización ".

Aquí la implementación

public class TaskSubmiter {
    private final ExecutorService executor;
    TaskSubmiter(ExecutorService executor) { this.executor = executor; }
    void doSomethingLarge(AnySourceClass source) {
        final List<InterestedResult> info = doPartialAsyncProcess(source);
        CompletionService<PartialResult> completionService = new ExecutorCompletionService<PartialResult>(executor);
        for (final InterestedResult interestedResultItem : info)
            completionService.submit(new Callable<PartialResult>() {
                public PartialResult call() {
                    return InterestedResult.doAnOperationToGetPartialResult();
                }
        });

    try {
        for (int t = 0, n = info.size(); t < n; t++) {
            Future<PartialResult> f = completionService.take();
            PartialResult PartialResult = f.get();
            processThisSegment(PartialResult);
            }
        } 
        catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } 
        catch (ExecutionException e) {
            throw somethinghrowable(e.getCause());
        }
    }
}
Alberto Gurrion
fuente
3

Esta es mi solución, basada en la sugerencia "AdamSkywalker", y funciona

package frss.main;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestHilos {

    void procesar() {
        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();

        System.out.println("FIN DEL PROCESO DE HILOS");
    }

    private List<Runnable> getTasks() {
        List<Runnable> tasks = new ArrayList<Runnable>();

        Hilo01 task1 = new Hilo01();
        tasks.add(task1);

        Hilo02 task2 = new Hilo02();
        tasks.add(task2);
        return tasks;
    }

    private class Hilo01 extends Thread {

        @Override
        public void run() {
            System.out.println("HILO 1");
        }

    }

    private class Hilo02 extends Thread {

        @Override
        public void run() {
            try {
                sleep(2000);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("HILO 2");
        }

    }


    public static void main(String[] args) {
        TestHilos test = new TestHilos();
        test.procesar();
    }
}
frss-soft.com
fuente
2

Podrías usar este código:

public class MyTask implements Runnable {

    private CountDownLatch countDownLatch;

    public MyTask(CountDownLatch countDownLatch {
         this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
         try {
             //Do somethings
             //
             this.countDownLatch.countDown();//important
         } catch (InterruptedException ex) {
              Thread.currentThread().interrupt();
         }
     }
}

CountDownLatch countDownLatch = new CountDownLatch(NUMBER_OF_TASKS);
ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
for (int i = 0; i < NUMBER_OF_TASKS; i++){
     taskExecutor.execute(new MyTask(countDownLatch));
}
countDownLatch.await();
System.out.println("Finish tasks");
Tuan Pham
fuente
2

Creé el siguiente ejemplo de trabajo. La idea es tener una manera de procesar un grupo de tareas (estoy usando una cola como ejemplo) con muchos subprocesos (determinado programáticamente por el número de tareas / umbral) y esperar hasta que todos los subprocesos se completen para continuar con algún otro procesamiento.

import java.util.PriorityQueue;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/** Testing CountDownLatch and ExecutorService to manage scenario where
 * multiple Threads work together to complete tasks from a single
 * resource provider, so the processing can be faster. */
public class ThreadCountDown {

private CountDownLatch threadsCountdown = null;
private static Queue<Integer> tasks = new PriorityQueue<>();

public static void main(String[] args) {
    // Create a queue with "Tasks"
    int numberOfTasks = 2000;
    while(numberOfTasks-- > 0) {
        tasks.add(numberOfTasks);
    }

    // Initiate Processing of Tasks
    ThreadCountDown main = new ThreadCountDown();
    main.process(tasks);
}

/* Receiving the Tasks to process, and creating multiple Threads
* to process in parallel. */
private void process(Queue<Integer> tasks) {
    int numberOfThreads = getNumberOfThreadsRequired(tasks.size());
    threadsCountdown = new CountDownLatch(numberOfThreads);
    ExecutorService threadExecutor = Executors.newFixedThreadPool(numberOfThreads);

    //Initialize each Thread
    while(numberOfThreads-- > 0) {
        System.out.println("Initializing Thread: "+numberOfThreads);
        threadExecutor.execute(new MyThread("Thread "+numberOfThreads));
    }

    try {
        //Shutdown the Executor, so it cannot receive more Threads.
        threadExecutor.shutdown();
        threadsCountdown.await();
        System.out.println("ALL THREADS COMPLETED!");
        //continue With Some Other Process Here
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }
}

/* Determine the number of Threads to create */
private int getNumberOfThreadsRequired(int size) {
    int threshold = 100;
    int threads = size / threshold;
    if( size > (threads*threshold) ){
        threads++;
    }
    return threads;
}

/* Task Provider. All Threads will get their task from here */
private synchronized static Integer getTask(){
    return tasks.poll();
}

/* The Threads will get Tasks and process them, while still available.
* When no more tasks available, the thread will complete and reduce the threadsCountdown */
private class MyThread implements Runnable {

    private String threadName;

    protected MyThread(String threadName) {
        super();
        this.threadName = threadName;
    }

    @Override
    public void run() {
        Integer task;
        try{
            //Check in the Task pool if anything pending to process
            while( (task = getTask()) != null ){
                processTask(task);
            }
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            /*Reduce count when no more tasks to process. Eventually all
            Threads will end-up here, reducing the count to 0, allowing
            the flow to continue after threadsCountdown.await(); */
            threadsCountdown.countDown();
        }
    }

    private void processTask(Integer task){
        try{
            System.out.println(this.threadName+" is Working on Task: "+ task);
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
}
}

¡Espero eso ayude!

Fernando Gil
fuente
1

Puede usar su propia subclase de ExecutorCompletionService para envolver taskExecutory su propia implementación de BlockingQueue para informarse cuando cada tarea se complete y realizar cualquier devolución de llamada u otra acción que desee cuando el número de tareas completadas alcance su objetivo deseado.

Alex Martelli
fuente
1

deberías usar executorService.shutdown()y executorService.awaitTerminationmétodo.

Un ejemplo de la siguiente manera:

public class ScheduledThreadPoolExample {

    public static void main(String[] args) throws InterruptedException {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
        executorService.scheduleAtFixedRate(() -> System.out.println("process task."),
                0, 1, TimeUnit.SECONDS);

        TimeUnit.SECONDS.sleep(10);
        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.DAYS);
    }

}
Rollen Holt
fuente
está esperando Terminación () necesaria después del apagado () /
gaurav
1

Así que publico mi respuesta de la pregunta vinculada aquí, en caso de que alguien quiera una forma más sencilla de hacer esto

ExecutorService executor = Executors.newFixedThreadPool(10);
CompletableFuture[] futures = new CompletableFuture[10];
int i = 0;
while (...) {
    futures[i++] =  CompletableFuture.runAsync(runner, executor);
}

CompletableFuture.allOf(futures).join(); // THis will wait until all future ready.
Mạnh Quyết Nguyễn
fuente
0

Java 8: podemos utilizar la API de transmisión para procesar la transmisión. Por favor, vea el fragmento a continuación

final List<Runnable> tasks = ...; //or any other functional interface
tasks.stream().parallel().forEach(Runnable::run) // Uses default pool

//alternatively to specify parallelism 
new ForkJoinPool(15).submit(
          () -> tasks.stream().parallel().forEach(Runnable::run) 
    ).get();
Vlad
fuente
2
Hola Vlad, bienvenido a StackOverflow. ¿Puede editar su respuesta para explicar cómo responde esto a la pregunta y qué hace el código? Las respuestas de código solo se desaconsejan aquí. ¡Gracias!
Tim Malone
Esta publicación habla de concurrencia. Paralelismo! = Concurrencia
GabrielBB
0

ExecutorService WORKER_THREAD_POOL 
  = Executors.newFixedThreadPool(10);
CountDownLatch latch = new CountDownLatch(2);
for (int i = 0; i < 2; i++) {
    WORKER_THREAD_POOL.submit(() -> {
        try {
            // doSomething();
            latch.countDown();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    });
}

// wait for the latch to be decremented by the two remaining threads
latch.await();

Si doSomething()lanzo algunas otras excepciones, latch.countDown()parece que no se ejecutará, ¿qué debo hacer?

Pengfei Zhan
fuente
0

si utiliza más hilos ExecutionServices SECUENCIALMENTE y desea esperar CADA SERVICIO DE EJECUCIÓN para finalizar. La mejor manera es como a continuación;

ExecutorService executer1 = Executors.newFixedThreadPool(THREAD_SIZE1);
for (<loop>) {
   executer1.execute(new Runnable() {
            @Override
            public void run() {
                ...
            }
        });
} 
executer1.shutdown();

try{
   executer1.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);

   ExecutorService executer2 = Executors.newFixedThreadPool(THREAD_SIZE2);
   for (true) {
      executer2.execute(new Runnable() {
            @Override
            public void run() {
                 ...
            }
        });
   } 
   executer2.shutdown();
} catch (Exception e){
 ...
}
Dr. X
fuente
-1

Esto puede ayudar

Log.i(LOG_TAG, "shutting down executor...");
executor.shutdown();
while (true) {
                try {
                    Log.i(LOG_TAG, "Waiting for executor to terminate...");
                    if (executor.isTerminated())
                        break;
                    if (executor.awaitTermination(5000, TimeUnit.MILLISECONDS)) {
                        break;
                    }
                } catch (InterruptedException ignored) {}
            }
Amol Desai
fuente
-1

Puede llamar a waitTillDone () en esta clase Runner :

Runner runner = Runner.runner(4); // create pool with 4 threads in thread pool

while(...) {
    runner.run(new MyTask()); // here you submit your task
}


runner.waitTillDone(); // and this blocks until all tasks are finished (or failed)


runner.shutdown(); // once you done you can shutdown the runner

Puede reutilizar esta clase y llamar a waitTillDone () tantas veces como desee antes de llamar a shutdown (), además su código es extremadamente simple . Además , no tiene que saber la cantidad de tareas por adelantado.

Para usarlo solo agregue este gradle / maven compile 'com.github.matejtymes:javafixes:1.3.1' dependencia de a su proyecto.

Más detalles se pueden encontrar aquí:

https://github.com/MatejTymes/JavaFixes

Matej Tymes
fuente
-2

Hay un método en el ejecutor getActiveCount(), que proporciona el recuento de subprocesos activos.

Después de atravesar el hilo, podemos verificar si el activeCount()valor es 0. Una vez que el valor es cero, significa que no hay hilos activos actualmente en ejecución, lo que significa que la tarea ha finalizado:

while (true) {
    if (executor.getActiveCount() == 0) {
    //ur own piece of code
    break;
    }
}
usuario
fuente
3
No es una buena idea, consulte stackoverflow.com/a/7271685/1166992 y el javadoc: "Devuelve el número aproximado de subprocesos que están ejecutando tareas activamente".
Olivier Faucheux