Qualche tempo fa ho scritto un post su interfacce Java Callable Future che possiamo utilizzare per ottenere i vantaggi del processing concorrente dei thread e che sono in grado di restituire un valore al programma chiamante. FutureTask è l’implementazione concreta di base dell’interfaccia Future e fornisce un processing asincrono. Contiene i metodi per avviare e annullare un compito e anche metodi che possono restituire lo stato di FutureTask, sia che sia completato o annullato. Abbiamo bisogno di un oggetto callable per creare un future task e poi possiamo utilizzare Java Thread Pool Executor per elaborarli in modo asincrono. Vediamo l’esempio di FutureTask con un programma semplice. Poiché FutureTask richiede un oggetto callable, creeremo una semplice implementazione Callable.
package com.journaldev.threads;
import java.util.concurrent.Callable;
public class MyCallable implements Callable {
private long waitTime;
public MyCallable(int timeInMillis){
this.waitTime=timeInMillis;
}
@Override
public String call() throws Exception {
Thread.sleep(waitTime);
// restituisci il nome del thread che esegue questo compito richiamabile
return Thread.currentThread().getName();
}
}
Ecco un esempio del metodo FutureTask e mostra i metodi comunemente utilizzati di FutureTask.
package com.journaldev.threads;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class FutureTaskExample {
public static void main(String[] args) {
MyCallable callable1 = new MyCallable(1000);
MyCallable callable2 = new MyCallable(2000);
FutureTask futureTask1 = new FutureTask(callable1);
FutureTask futureTask2 = new FutureTask(callable2);
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(futureTask1);
executor.execute(futureTask2);
while (true) {
try {
if(futureTask1.isDone() && futureTask2.isDone()){
System.out.println("Done");
// arresta il servizio dell'esecutore
executor.shutdown();
return;
}
if(!futureTask1.isDone()){
// aspetta indefinitamente che il compito futuro sia completato
System.out.println("FutureTask1 output="+futureTask1.get());
}
System.out.println("Waiting for FutureTask2 to complete");
String s = futureTask2.get(200L, TimeUnit.MILLISECONDS);
if(s !=null){
System.out.println("FutureTask2 output="+s);
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}catch(TimeoutException e){
// non fare nulla
}
}
}
}
Quando eseguiamo il programma sopra, noterai che non stampa nulla per un po’ perché il metodo get()
di FutureTask attende che il compito venga completato e poi restituisce l’oggetto di output. C’è anche un metodo sovraccaricato per attendere solo per un determinato periodo di tempo e lo stiamo usando per futureTask2. Nota anche l’uso del metodo isDone()
per assicurarsi che il programma venga terminato una volta eseguiti tutti i compiti. L’output del programma sopra sarà:
FutureTask1 output=pool-1-thread-1
Waiting for FutureTask2 to complete
Waiting for FutureTask2 to complete
Waiting for FutureTask2 to complete
Waiting for FutureTask2 to complete
Waiting for FutureTask2 to complete
FutureTask2 output=pool-1-thread-2
Done
In realtà non c’è alcun beneficio di FutureTask, ma è utile quando vogliamo sovrascrivere alcuni metodi dell’interfaccia Future e non vogliamo implementare ogni metodo dell’interfaccia Future.
Source:
https://www.digitalocean.com/community/tutorials/java-futuretask-example-program