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:65)
Caused by: Java.lang.NullPointerException at
com.mmt.freedom.cheapestfare.HotelCheapestFare.getHotelCheapestFare(HotelCheapestFare.Java:166)
at com.mmt.freedom.cheapestfare.HotelCheapestFare.call(HotelCheapestFare.Java:219)
at com.mmt.freedom.cheapestfare.HotelCheapestFare.call(HotelCheapestFare.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(Unknown Source)
at Java.lang.Thread.run
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 Future
s à 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 Future
s de la méthode submit
, puis de les parcourir pour voir si tout est terminé.
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
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.