Si utilizar invokeAll o submit – java Executor service

Tengo un escenario donde tengo que ejecutar 5 subprocesos asincrónicamente para el mismo invocable. Por lo que yo entiendo, hay dos opciones:

1) usando enviar (llamable)

ExecutorService executorService = Executors.newFixedThreadPool(5); List<Future> futures = new ArrayList(); for(Callable callableItem: myCallableList){ futures.add(executorService.submit(callableItem)); } 

2) usando invokeAll (Colecciones de Callable)

 ExecutorService executorService = Executors.newFixedThreadPool(5); List<Future> futures = executorService.invokeAll(myCallableList)); 
  1. ¿Cuál debería ser la forma preferida?
  2. ¿Hay alguna desventaja o impacto en el rendimiento en alguno de ellos en comparación con el otro?

Opción 1 : está enviando las tareas a ExecutorService y no espera la finalización de todas las tareas, que se han enviado a ExecutorService

Opción 2 : está esperando la finalización de todas las tareas, que se han enviado a ExecutorService .

¿Cuál debería ser la forma preferida?

Dependiendo de los requisitos de la aplicación, se prefiere cualquiera de ellos.

  1. Si no espera después de enviar la tarea () a ExecutorService , prefiera la Option 1 .
  2. Si necesita esperar a que se completen todas las tareas que se enviaron a ExecutorService , prefiera la Option 2 .

¿Hay alguna desventaja o impacto en el rendimiento en alguno de ellos en comparación con el otro?

Si su aplicación exige la Opción 2, debe esperar la finalización de todas las tareas enviadas a ExecutorService diferencia de la Opción 1. El rendimiento no es un criterio de comparación, ya que ambos están diseñados para dos propósitos diferentes.

Y una cosa más importante: cualquier opción que prefiera, FutureTask traga Excepciones durante la ejecución de la tarea. Tienes que tener cuidado. Eche un vistazo a esta pregunta de SE: Manejo de excepciones para ThreadPoolExecutor

Con Java 8, tiene una opción más: ExecutorCompletionService

Un CompletionService que utiliza un Executor suministrado para ejecutar tareas. Esta clase organiza que las tareas enviadas sean, una vez completadas, colocadas en una cola accesible usando take. La clase es lo suficientemente liviana para ser adecuada para uso transitorio al procesar grupos de tareas.

Eche un vistazo a la pregunta SE relacionada: ExecutorCompletionService? ¿Por qué necesitamos uno si tenemos invocar todos?

EDITAR:

En realidad hay una diferencia entre ellos. Por alguna razón, invokeAll() llamará a get() para cada future producido. Por lo tanto, esperará a que las tareas terminen y es por eso que puede lanzar InterruptedException (mientras que submit() no arroja nada).

Ese es el Javadoc para el método invokeAll() :

Ejecuta las tareas dadas, devolviendo una lista de futuros que mantienen su estado y resultados cuando todo se completa .

Entonces, ambas estrategias básicamente hacen lo mismo, pero si llamas a invokeAll() serás bloqueado hasta que todas las tareas estén hechas.


Respuesta original (incompleta):

El método invokeAll() está ahí exactamente para situaciones como estas. Definitivamente deberías usarlo.

Sin embargo, realmente no es necesario crear una instancia de esa List :

 ExecutorService executorService = Executors.newFixedThreadPool(5); List> futures = executorService.invokeAll(myCallableList)); 

Esto debería ser suficiente, y se ve mucho más limpio que la primera alternativa.

Supongamos que tiene una tarea cuyo resultado depende del número de tareas ejecutables independientemente. Pero para que la tarea inicial te complete solo tienes un tiempo limitado. Al igual que es una llamada API.

Entonces, por ejemplo, tiene 100ms para completar tareas de nivel superior y también hay 10 tareas dependientes. Para eso, si está usando un envío aquí, verá cómo se verá el código.

 List tasks = []// assume contains sub tasks List futures = [] for(Callable task: tasks) { futures.add(service.submit(task)); } for(Future futute: futures) { future.get(100, TimeUnit.MILLISECONDS); } 

Entonces, si cada una de las tareas secundarias tardara exactamente 50 ms en completarse, la pieza de código anterior tardaría 50 ms. Pero si cada una de las sub tareas tomara 1000 ms para completar lo anterior, tomaría 100 * 10 = 1000 ms o 1s. Esto dificulta el cálculo del tiempo total para que sea inferior a 100 ms para todas las subtareas.

El método invokeAll nos ayuda en tal escenario

 List futures = service.invokeall(tasks, 100, TimeUnit.MILLISECONDS) for(Future future: futures) { if(!future.isCancelled()) { results.add(future.get()); } } 

De esta manera, el tiempo máximo que tomaría es de 100 ms, incluso si las subtareas individuales tomaran más que eso.