¿Cuál es la forma más sencilla de esperar ExecutorService
a que finalicen todas las tareas ? Mi tarea es principalmente computacional, por lo que solo quiero ejecutar una gran cantidad de trabajos, uno en cada núcleo. En este momento mi configuración se ve así:
ExecutorService es = Executors.newFixedThreadPool(2);
for (DataTable singleTable : uniquePhrases) {
es.execute(new ComputeDTask(singleTable));
}
try{
es.wait();
}
catch (InterruptedException e){
e.printStackTrace();
}
ComputeDTask
Implementos ejecutables. Esto aparece para ejecutar las tareas correctamente, pero el código se bloquea en wait()
la IllegalMonitorStateException
. Esto es extraño, porque jugué con algunos ejemplos de juguetes y parecía funcionar.
uniquePhrases
contiene varias decenas de miles de elementos. ¿Debo estar usando otro método? Estoy buscando algo lo mas simple posible
java
multithreading
threadpool
executorservice
george smiley
fuente
fuente
es
) cuando desea esperarlo; el bloqueo se liberará automáticamente mientras esperaExecutors.newFixedThreadPool(System.getRuntime().availableProcessors());
Respuestas:
El enfoque más simple es usar el
ExecutorService.invokeAll()
que hace lo que quieres en una sola línea. En su lenguaje, necesitará modificar o ajustarComputeDTask
para implementarCallable<>
, lo que puede brindarle un poco más de flexibilidad. Probablemente en su aplicación hay una implementación significativa deCallable.call()
, pero aquí hay una manera de envolverla si no la está usandoExecutors.callable()
.Como otros han señalado, puede usar la versión de tiempo de espera de,
invokeAll()
si corresponde. En este ejemplo,answers
va a contener un montón deFuture
s que devolverán valores nulos (consulte la definición deExecutors.callable()
. Probablemente lo que quiera hacer es una ligera refactorización para que pueda obtener una respuesta útil o una referencia al subyacenteComputeDTask
, pero puedo No cuentes con tu ejemplo.Si no está claro, tenga en cuenta que
invokeAll()
no volverá hasta que se completen todas las tareas. (es decir, todos los mensajes de correo electrónico deFuture
suanswers
colección informarán.isDone()
si se le solicita). Esto evita todo el apagado manual, esperar la terminación, etc. y le permite volver a usarloExecutorService
ordenadamente durante varios ciclos, si lo desea.Hay algunas preguntas relacionadas sobre SO:
Cómo esperar a que todos los hilos terminen
Valores de retorno de hilos Java
invokeAll () no está dispuesto a aceptar una Colección <invocable <t>>
¿Necesito sincronizar?
Ninguno de estos es estrictamente adecuado para su pregunta, pero proporcionan un poco de color sobre cómo la gente piensa
Executor
/ExecutorService
debe ser utilizado.fuente
Si desea esperar a que se completen todas las tareas, use el
shutdown
método en lugar dewait
. Luego sígalo conawaitTermination
.Además, puede usar
Runtime.availableProcessors
para obtener el número de subprocesos de hardware para que pueda inicializar su conjunto de subprocesos correctamente.fuente
awaitTermination
requiere tiempo de espera como parámetro. Si bien es posible proporcionar un tiempo finito y colocar un bucle a su alrededor para esperar hasta que todos los hilos hayan terminado, me preguntaba si había una solución más elegante.Si esperar
ExecutorService
a que finalicen todas las tareas no es precisamente su objetivo, sino esperar hasta que se haya completado un lote específico de tareas , puede utilizar unCompletionService
- específicamente, unExecutorCompletionService
.La idea es crear una
ExecutorCompletionService
envolturaExecutor
, enviar una cantidad conocida de tareas a través deCompletionService
, y luego extraer esa misma cantidad de resultados de la cola de finalización utilizandotake()
(qué bloques) opoll()
(qué no). Una vez que haya dibujado todos los resultados esperados correspondientes a las tareas que envió, sabrá que todos están listos.Permítanme decir esto una vez más, porque no es obvio desde la interfaz: debe saber cuántas cosas pone en el
CompletionService
para poder saber cuántas cosas tratar de extraer. Esto importa especialmente con eltake()
método: llámelo una vez más y bloqueará su hilo de llamada hasta que otro hilo envíe otro trabajo al mismoCompletionService
.Hay algunos ejemplos que muestran cómo usar
CompletionService
en el libro Java Concurrency in Practice .fuente
CompletionService
Si desea esperar a que el servicio del ejecutor termine de ejecutarse, llame
shutdown()
y luego, espere Terminación (unidades, tipo de unidad) , por ejemploawaitTermination(1, MINUTE)
. El ExecutorService no bloquea en su propio monitor, por lo que no puede usar,wait
etc.fuente
awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
Podría esperar a que finalicen los trabajos en un intervalo determinado:
O puede usar ExecutorService . submit ( Runnable ) y recoge los objetos Future que devuelve y llama a get () en cada turno para esperar a que finalicen.
InterruptedException es extremadamente importante para manejar adecuadamente. Es lo que le permite a usted o a los usuarios de su biblioteca finalizar un proceso largo de forma segura.
fuente
Solo usa
En cada hilo
y como barrera
fuente
Causa raíz de IllegalMonitorStateException :
Desde su código, acaba de llamar a wait () en ExecutorService sin poseer el bloqueo.
Debajo del código arreglará
IllegalMonitorStateException
Siga uno de los siguientes enfoques para esperar la finalización de todas las tareas que se han enviado
ExecutorService
.Itere a través de todas las
Future
tareas desdesubmit
encendidoExecutorService
y verifique el estado bloqueando la llamadaget()
en elFuture
objetoUsando invokeAll en
ExecutorService
Usando CountDownLatch
Usando ForkJoinPool o newWorkStealingPool de
Executors
(desde java 8)Cierre el grupo como se recomienda en la página de documentación de Oracle
Si desea esperar con gracia la finalización de todas las tareas cuando utiliza la opción 5 en lugar de las opciones 1 a 4, cambie
a
un
while(condition)
que verifica cada 1 minuto.fuente
Puede usar el
ExecutorService.invokeAll
método, ejecutará todas las tareas y esperará hasta que todos los hilos terminen su tarea.Aquí está completo javadoc
También puede usar una versión sobrecargada de este método para especificar el tiempo de espera.
Aquí hay un código de muestra con
ExecutorService.invokeAll
fuente
También tengo la situación de que tengo que rastrear un conjunto de documentos. Comienzo con un documento inicial "inicial" que debe procesarse, ese documento contiene enlaces a otros documentos que también deben procesarse, y así sucesivamente.
En mi programa principal, solo quiero escribir algo como lo siguiente, donde
Crawler
controla un montón de hilos.La misma situación sucedería si quisiera navegar por un árbol; aparecería en el nodo raíz, el procesador para cada nodo agregaría hijos a la cola según sea necesario, y un grupo de subprocesos procesaría todos los nodos en el árbol, hasta que no hubiera más.
No pude encontrar nada en la JVM que me pareció un poco sorprendente. Así que escribí una clase
ThreadPool
que se puede usar directamente o subclase para agregar métodos adecuados para el dominio, por ejemploschedule(Document)
. ¡Espero eso ayude!ThreadPool Javadoc | Maven
fuente
Agregue todos los hilos en la colección y envíelo usando
invokeAll
. Si puede usar elinvokeAll
método deExecutorService
, JVM no procederá a la siguiente línea hasta que todos los hilos estén completos.Aquí hay un buen ejemplo: invokeAll a través de ExecutorService
fuente
Envíe sus tareas al Runner y luego espere llamando al método waitTillDone () de esta manera:
Para usarlo agregue esta dependencia gradle / maven:
'com.github.matejtymes:javafixes:1.0'
Para obtener más detalles, consulte aquí: https://github.com/MatejTymes/JavaFixes o aquí: http://matejtymes.blogspot.com/2016/04/executor-that-notifies-you-when-task.html
fuente
Una alternativa simple a esto es usar hilos junto con join. Consulte: Unir hilos
fuente
Solo esperaré a que el ejecutor termine con un tiempo de espera especificado que crees que es adecuado para completar las tareas.
fuente
Suena como lo que necesita
ForkJoinPool
y utiliza el grupo global para ejecutar tareas.La belleza está en
pool.awaitQuiescence
donde el método bloqueará, utilice el hilo de la persona que llama para ejecutar sus tareas y luego regrese cuando esté realmente vacío.fuente