Ma question: Comment exécuter un groupe d'objets threadés sur un ThreadPoolExecutor
et attendre qu'ils finissent tous avant de continuer?
Je suis nouveau sur ThreadPoolExecutor. Donc, ce code est un test pour apprendre comment cela fonctionne. À l'heure actuelle, je ne remplis même pas la BlockingQueue
avec les objets car je ne comprends pas comment démarrer la file d'attente sans appeler execute()
avec une autre RunnableObject
. Quoi qu'il en soit, pour l'instant, j'appelle simplement awaitTermination()
mais je pense qu'il me manque encore quelque chose. Tous les conseils seraient super! Merci.
public void testThreadPoolExecutor() throws InterruptedException {
int limit = 20;
BlockingQueue q = new ArrayBlockingQueue(limit);
ThreadPoolExecutor ex = new ThreadPoolExecutor(limit, limit, 20, TimeUnit.SECONDS, q);
for (int i = 0; i < limit; i++) {
ex.execute(new RunnableObject(i + 1));
}
ex.awaitTermination(2, TimeUnit.SECONDS);
System.out.println("finished");
}
La classe RunnableObject:
package playground;
public class RunnableObject implements Runnable {
private final int id;
public RunnableObject(int id) {
this.id = id;
}
@Override
public void run() {
System.out.println("ID: " + id + " started");
try {
Thread.sleep(2354);
} catch (InterruptedException ignore) {
}
System.out.println("ID: " + id + " ended");
}
}
Vous devriez boucler sur awaitTermination
ExecutorService threads;
// ...
// Tell threads to finish off.
threads.shutdown();
// Wait for everything to finish.
while (!threads.awaitTermination(10, TimeUnit.SECONDS)) {
log.info("Awaiting completion of threads.");
}
Votre problème semble être que vous n'appelez pas shutdown
après avoir soumis tous les travaux à votre pool. Sans shutdown()
votre awaitTermination
retournera toujours faux.
ThreadPoolExecutor ex =
new ThreadPoolExecutor(limit, limit, 20, TimeUnit.SECONDS, q);
for (int i = 0; i < limit; i++) {
ex.execute(new RunnableObject(i + 1));
}
// you are missing this line!!
ex.shutdown();
ex.awaitTermination(2, TimeUnit.SECONDS);
Vous pouvez également utiliser l'une des méthodes suivantes pour attendre que tous vos travaux soient terminés:
List<Future<Object>> futures = new ArrayList<Future<Object>>();
for (int i = 0; i < limit; i++) {
futures.add(ex.submit(new RunnableObject(i + 1), (Object)null));
}
for (Future<Object> future : futures) {
// this joins with the submitted job
future.get();
}
...
// still need to shutdown at the end
ex.shutdown();
De plus, étant donné que vous dormez pendant 2354
millisecondes mais que vous attendez uniquement la fin de tous les travaux pour 2
SECONDS
, awaitTermination
retournera toujours false
.
Enfin, il semble que vous vous inquiétiez de la création d'une nouvelle ThreadPoolExecutor
et que vous souhaitiez plutôt réutiliser la première. Ne sois pas. Le temps système du GC sera extrêmement minime comparé à tout code que vous écrivez pour détecter si les travaux sont terminés.
Pour citer les javadocs, ThreadPoolExecutor.shutdown()
:
Lance un arrêt normal dans lequel les tâches soumises précédemment sont exécutées, mais aucune nouvelle tâche ne sera acceptée. L'invocation n'a aucun effet supplémentaire si elle est déjà fermée.
Dans la méthode ThreadPoolExecutor.awaitTermination(...)
, il attend que l'état de l'exécuteur passe à TERMINATED
. Mais d’abord, l’état doit passer à SHUTDOWN
si shutdown()
est appelé ou STOP
si shutdownNow()
est appelé.
Cela n'a rien à voir avec l'exécuteur lui-même. Il suffit d'utiliser l'interface Java.util.concurrent.ExecutorService.invokeAll(Collection<? extends Callable<T>>)
. Il bloquera jusqu'à ce que toutes les Callable
s soient terminées.
Les exécuteurs sont censés vivre longtemps; au-delà de la vie d'un groupe de tâches. shutdown
est pour quand l'application est terminée et nettoyer.
Voici une variante de la réponse acceptée qui gère les tentatives si/quand une exception InterruptedException est levée:
executor.shutdown();
boolean isWait = true;
while (isWait)
{
try
{
isWait = !executor.awaitTermination(10, TimeUnit.SECONDS);
if (isWait)
{
log.info("Awaiting completion of bulk callback threads.");
}
} catch (InterruptedException e) {
log.debug("Interruped while awaiting completion of callback threads - trying again...");
}
}
Une autre approche consiste à utiliser CompletionService, très utile si vous devez essayer n'importe quel résultat de tâche:
//run 3 task at time
final int numParallelThreads = 3;
//I used newFixedThreadPool for convenience but if you need you can use ThreadPoolExecutor
ExecutorService executor = Executors.newFixedThreadPool(numParallelThreads);
CompletionService<String> completionService = new ExecutorCompletionService<String>(executor);
int numTaskToStart = 15;
for(int i=0; i<numTaskToStart ; i++){
//task class that implements Callable<String> (or something you need)
MyTask mt = new MyTask();
completionService.submit(mt);
}
executor.shutdown(); //it cannot be queued more task
try {
for (int t = 0; t < numTaskToStart ; t++) {
Future<String> f = completionService.take();
String result = f.get();
// ... something to do ...
}
} catch (InterruptedException e) {
//termination of all started tasks (it returns all not started tasks in queue)
executor.shutdownNow();
} catch (ExecutionException e) {
// ... something to catch ...
}