2009-08-24 13 views
14

Je dois soumettre un certain nombre de tâches et les attendre jusqu'à ce que tous les résultats soient disponibles. Chacun d'entre eux ajoute un String à un Vector (qui est synchronisé par défaut). Ensuite, je dois commencer une nouvelle tâche pour chaque résultat dans le vecteur, mais je dois le faire uniquement lorsque toutes les tâches précédentes ont cessé de faire leur travail. Je veux utiliser Java Executor, en particulier j'ai essayé d'utiliser Executors.newFixedThreadPool(100) afin d'utiliser un nombre fixe de thread (j'ai un nombre variable de tâches qui peut être de 10 ou 500) mais je suis nouveau avec les exécuteurs et moi Je ne sais pas comment attendre la fin d'une tâche. Ce est quelque chose comme un pseudo-code de ce que mon programme doit faire:Exécuteurs Java: attente de la fin de la tâche.

ExecutorService e = Executors.newFixedThreadPool(100); 
while(true){ 

/*do something*/ 

for(...){ 
<start task> 
} 

<wait for all task termination> 

for each String in result{ 
<start task> 
} 

<wait for all task termination> 
} 

Je ne peux pas faire e.shutdown parce que je suis dans un certain temps (vrai) et je dois réutiliser le executorService ..

Pouvez-vous m'aider? Pouvez-vous me suggérer un guide/livre sur les exécuteurs java?

+0

Jetez un oeil à http://stackoverflow.com/questions/1228433/java-parallel-work-iterator/1228445 – Tim

Répondre

21

Le ExecutorService vous donne un mécanisme pour exécuter plusieurs tâches simultanément et récupérer une collection d'objets Future (représentant le calcul asynchrone de la tâche).

Collection<Callable<?>> tasks = new LinkedList<Callable<?>>(); 
//populate tasks 
for (Future<?> f : executorService.invokeAll(tasks)) { //invokeAll() blocks until ALL tasks submitted to executor complete 
    f.get(); 
} 

Si vous avez Runnable s au lieu de Callable s, vous pouvez facilement transformer un Runnable en Callable<Object> en utilisant la méthode:

Callable<?> c = Executors.callable(runnable); 
+4

Désolé pour avoir déterré un ancien message, mais je ne vois pas l'intérêt d'appeler f.get(); invokeAll() bloque lui-même, il n'est donc pas nécessaire d'appeler get() à moins que le résultat ne vous intéresse (ce qui n'est pas le cas de votre code). – hooch

+0

@hooch: IMO vous devriez toujours appeler 'get()' dans le cas où votre 'Callable' a lancé une exception. Sinon, tout est perdu. –

2

Lorsque vous soumettez à un service d'exécution, vous récupérez un objet Future.

Stockez ces objets dans une collection, puis appelez le get() à tour de rôle. get() jusqu'à ce que le travail sous-jacent se termine, et le résultat est que l'appel get() sur chacun se terminera une fois que tous les travaux sous-jacents ont terminé.

par exemple.

Collection<Future> futures = ... 
for (Future f : futures) { 
    Object result = f.get(); 
    // maybe do something with the result. This could be a 
    // genericised Future<T> 
} 
System.out.println("Tasks completed"); 

Une fois que tout cela est terminé, commencez votre deuxième soumission. Notez qu'il ne s'agit peut-être pas d'une utilisation optimale de votre pool de threads, car il deviendra inactif, puis vous le remplissez à nouveau. Si possible, essayez et gardez-le occupé à faire des choses.

+0

Quelle est la différence entre e.submit (je pense que je dois l'utiliser en suivant votre exemple) et e .exécuter?? – Raffo

+0

La différence est qu'avec submit vous récupérez un Future et avec execute vous ne le faites pas. Si vous utilisez votre propre 'ThreadFactory' avec un' UncaughtExceptionHandler', alors 'execute' amènera le gestionnaire à recevoir des exceptions non interceptées, alors que' submit' ne le fera pas - vous obtiendriez seulement les exceptions via le 'Future' 'get 'méthode –

14

Pouvez-vous me suggérer un guide/livre sur java exécuteurs?

Je peux répondre à cette partie:

Java Concurrency in Practice par Brian Goetz (avec Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes et Doug Lea) est très probablement votre meilleur pari.

Ce n'est pas seulement sur les exécuteurs bien, mais couvre plutôt paquet java.util.concurrent en général, ainsi que des concepts de base et les techniques simultanéité, et certains sujets avancés tels que le modèle de mémoire Java.

+0

Ceci est un excellent livre, mais pas vraiment un pour les débutants. –

14

Plutôt que de soumettre Runnable s ou Callable s à un Executor directement et le stockage des Future valeurs de retour correspondant, je vous recommande d'utiliser chaque Futureune implémentation CompletionService pour récupérer quand il complète. Cette approche découple la production de tâches à partir de la consommation de tâches terminées, permettant par exemple de nouvelles tâches à partir d'un fil de production sur une période de temps.

Collection<Callable<Result>> workItems = ... 
ExecutorService executor = Executors.newSingleThreadExecutor(); 
CompletionService<Result> compService = new ExecutorCompletionService<Result>(executor); 

// Add work items to Executor. 
for (Callable<Result> workItem : workItems) { 
    compService.submit(workItem); 
} 

// Consume results as they complete (this would typically occur on a different thread). 
for (int i=0; i<workItems.size(); ++i) { 
    Future<Result> fut = compService.take(); // Will block until a result is available. 
    Result result = fut.get(); // Extract result; this will not block. 
} 
+0

En pratique, cela implique plus de LOC que mon exemple ci-dessus. Un CompletionService est généralement utile si vous soumettez des tâches à partir de plusieurs emplacements, mais que vous souhaitez gérer les tâches de manière cohérente (par exemple, pour effectuer d'autres calculs) - que vous souhaitez uniquement définir une fois. –

+1

@oxbow: Ou si vous souhaitez commencer le traitement des résultats dès que la première tâche est terminée! Sinon, vous pourriez être en attente de votre tâche la plus lente tandis que les autres sont déjà fait .. (Adamski +1) – Tim

+1

@Tim - L'OP a dit très clairement qu'il voulait attendre que toutes les tâches soient terminées, donc cela ne fait aucune différence (autre que quelques nanosecondes) dont la tâche se termine en premier. –

1
ExecutorService executor = ... 
//submit tasks 
executor.shutdown(); // previously submitted tasks are executed, 
        // but no new tasks will be accepted 
while(!executor.awaitTermination(1, TimeUnit.SECONDS)) 
    ; 

Il n'y a pas moyen facile de faire ce que vous voulez sans créer ExecutorService personnalisé.