web-dev-qa-db-fra.com

Comment utiliser invokeAll () pour laisser tout le pool de threads faire leur tâche?

    ExecutorService pool=Executors.newFixedThreadPool(7);
        List<Future<Hotel>> future=new ArrayList<Future<Hotel>>();
        List<Callable<Hotel>> callList = new ArrayList<Callable<Hotel>>();

        for(int i=0;i<=diff;i++){

            String str="2013-"+(liDates.get(i).get(Calendar.MONTH)+1)+"-"+liDates.get(i).get(Calendar.DATE);

            callList.add(new HotelCheapestFare(str));

        }       
     future=pool.invokeAll(callList);
for(int i=0;i<=future.size();i++){

        System.out.println("name is:"+future.get(i).get().getName());
    }

Maintenant, je veux que le pool invokeAll toutes les tâches avant d'accéder à la boucle for mais lorsque j'exécute ce programme, la boucle for soit exécutée avant invokeAll et lève cette exception:

Java.util.concurrent.ExecutionException: Java.lang.NullPointerException at 
Java.util.concurrent.FutureTask$Sync.innerGet(Unknown Source) at  
Java.util.concurrent.FutureTask.get(Unknown Source) at 
com.mmt.freedom.cheapestfare.TestHotel.main(TestHotel.Java:6‌​5)

Caused by: Java.lang.NullPointerException at 
com.mmt.freedom.cheapestfare.HotelCheapestFare.getHotelCheap‌estFare(HotelCheapes‌​tFare.Java:166) 
at com.mmt.freedom.cheapestfare.HotelCheapestFare.call(HotelChe‌​apestFare.Java:219)
at com.mmt.freedom.cheapestfare.HotelCheapestFare.call(HotelChe‌​apestFare.Java:1) 
at Java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source) at Java.util.concurrent.FutureTask.run(Unknown Source) 
at Java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) atjava.util.concurrent.ThreadPoolExecutor$Worker.run(Unknow‌​n Source)
at Java.lang.Thread.run
21
user1948313

Un ExecutorService fonctionne de la manière suivante: lorsque vous appelez invokeAll, il attend que toutes les tâches se terminent:

Exécute les tâches données, renvoyant une liste des Futures contenant leur statut et leurs résultats lorsque tous sont terminés. Future.isDone () est vrai pour chaque élément de la liste renvoyée. Notez qu'une tâche terminée aurait pu se terminer normalement ou en lançant une exception . Les résultats de cette méthode ne sont pas définis si la collection donnée est modifiée pendant que cette opération est en cours.1(pas d'italique dans l'original)

Cela signifie que vos tâches sont toutes terminées, mais certaines peuvent avoir levé une exception. Cette exception fait partie de Future - l'appel de get entraîne la reconduction de l'exception dans un ExecutionException.

De votre stacktrack

Java.util.concurrent.ExecutionException: Java.lang.NullPointerException at
Java.util.concurrent.FutureTask$Sync.innerGet(Unknown Source) at
Java.util.concurrent.FutureTask.get(Unknown Source) at 
                                ^^^ <-- from get

Vous pouvez voir que c'est effectivement le cas. Une de vos tâches a échoué avec un NPE. ExecutorService a intercepté l'exception et vous en informe en lançant un ExecutionException lorsque vous appelez Future.get.

Maintenant, si vous voulez prendre des tâches comme elles se terminent vous avez besoin d'un ExecutorCompletionService. Cela agit comme un BlockingQueue qui vous permettra d'interroger les tâches au fur et à mesure qu'elles se terminent.

public static void main(String[] args) throws Exception {
    final ExecutorService executorService = Executors.newFixedThreadPool(10);
    final ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(executorService);
    executorService.submit(new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i < 100; ++i) {
                try {
                    final Future<String> myValue = completionService.take();
                    //do stuff with the Future
                    final String result = myValue.get();
                    System.out.println(result);
                } catch (InterruptedException ex) {
                    return;
                } catch (ExecutionException ex) {
                    System.err.println("TASK FAILED");
                }
            }
        }
    });
    for (int i = 0; i < 100; ++i) {
        completionService.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                if (Math.random() > 0.5) {
                    throw new RuntimeException("FAILED");
                }
                return "SUCCESS";
            }
        });
    }
    executorService.shutdown();
}

Dans cet exemple, j'ai une tâche qui appelle take sur le ExecutorCompletionService qui obtient les Futures à mesure qu'ils deviennent disponibles, puis je soumets des tâches au ExecutorCompletionService.

Cela vous permettra d'obtenir la tâche ayant échoué dès qu'elle échoue plutôt que d'avoir à attendre que toutes les tâches échouent ou réussissent ensemble.

La seule complication est qu'il est difficile de dire au thread d'interrogation que toutes les tâches sont effectuées car tout est désormais asynchrone. Dans ce cas, j'ai utilisé la connaissance que 100 tâches seront soumises, de sorte qu'il n'a qu'à interroger 100 fois. Une manière plus générale serait de collecter également les Futures de la méthode submit, puis de les parcourir pour voir si tout est terminé.

20
Boris the Spider

Future.get () lève les exceptions ci-dessous.

CancellationException - si le calcul a été annulé

ExecutionException - si le calcul a levé une exception

InterruptedException - si le thread courant a été interrompu en attendant

Attrapez toutes ces exceptions lorsque vous appelez la méthode get().

J'ai simulé la division par zéro exception pour certaines tâches Callable mais l'exception dans une Callable n'affecte pas les autres tâches Callable soumises à ExecutorService si vous en attrapez plus de trois exceptions comme indiqué dans l'exemple de code.

Exemple d'extrait de code:

import Java.util.concurrent.*;
import Java.util.*;

public class InvokeAllUsage{
    public InvokeAllUsage(){
        System.out.println("creating service");
        ExecutorService service = Executors.newFixedThreadPool(10);

        List<MyCallable> futureList = new ArrayList<MyCallable>();
        for ( int i=0; i<10; i++){
            MyCallable myCallable = new MyCallable((long)i+1);
            futureList.add(myCallable);
        }
        System.out.println("Start");
        try{
            List<Future<Long>> futures = service.invokeAll(futureList);  
            for(Future<Long> future : futures){
                try{
                    System.out.println("future.isDone = " + future.isDone());
                    System.out.println("future: call ="+future.get());
                }
                catch (CancellationException ce) {
                    ce.printStackTrace();
                } catch (ExecutionException ee) {
                    ee.printStackTrace();
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt(); // ignore/reset
                }
            }
        }catch(Exception err){
            err.printStackTrace();
        }
        System.out.println("Completed");
        service.shutdown();
    }
    public static void main(String args[]){
        InvokeAllUsage demo = new InvokeAllUsage();
    }
    class MyCallable implements Callable<Long>{
        Long id = 0L;
        public MyCallable(Long val){
            this.id = val;
        }
        public Long call(){

            if ( id % 5 == 0){
                id = id / 0;
            }           
            return id;
        }
    }
}

production:

creating service
Start
future.isDone = true
future: call =1
future.isDone = true
future: call =2
future.isDone = true
future: call =3
future.isDone = true
future: call =4
future.isDone = true
Java.util.concurrent.ExecutionException: Java.lang.ArithmeticException: / by zero
        at Java.util.concurrent.FutureTask.report(FutureTask.Java:122)
        at Java.util.concurrent.FutureTask.get(FutureTask.Java:188)
        at InvokeAllUsage.<init>(InvokeAllUsage.Java:20)
        at InvokeAllUsage.main(InvokeAllUsage.Java:37)
Caused by: Java.lang.ArithmeticException: / by zero
        at InvokeAllUsage$MyCallable.call(InvokeAllUsage.Java:47)
        at InvokeAllUsage$MyCallable.call(InvokeAllUsage.Java:39)
        at Java.util.concurrent.FutureTask.run(FutureTask.Java:262)
        at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1145)
        at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:615)
        at Java.lang.Thread.run(Thread.Java:744)
future.isDone = true
future: call =6
future.isDone = true
future: call =7
future.isDone = true
future: call =8
future.isDone = true
future: call =9
future.isDone = true
Java.util.concurrent.ExecutionException: Java.lang.ArithmeticException: / by zero
        at Java.util.concurrent.FutureTask.report(FutureTask.Java:122)
        at Java.util.concurrent.FutureTask.get(FutureTask.Java:188)
        at InvokeAllUsage.<init>(InvokeAllUsage.Java:20)
        at InvokeAllUsage.main(InvokeAllUsage.Java:37)
Caused by: Java.lang.ArithmeticException: / by zero
        at InvokeAllUsage$MyCallable.call(InvokeAllUsage.Java:47)
        at InvokeAllUsage$MyCallable.call(InvokeAllUsage.Java:39)
        at Java.util.concurrent.FutureTask.run(FutureTask.Java:262)
        at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1145)
        at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:615)
        at Java.lang.Thread.run(Thread.Java:744)
Completed
6
Ravindra babu

invokeAll est une méthode de blocage. Cela signifie que la JVM ne passera pas à la ligne suivante tant que tous les threads ne seront pas terminés. Je pense donc qu'il y a quelque chose qui ne va pas avec le résultat futur de votre thread.

System.out.println("name is:"+future.get(i).get().getName());

à partir de cette ligne, je pense qu'il y a des futurs qui n'ont aucun résultat et peuvent être nuls, donc vous devriez vérifier votre code, s'il y a des futurs nuls, si c'est le cas, obtenez un if avant l'exécution de cette ligne.

0
Winston