Utilizzando Future possiamo scoprire lo stato dell'attività Callable e ottenere l'oggetto restituito. Fornisce il metodo get() che può aspettare che il Callable termini e quindi restituire il risultato.
Future fornisce il metodo cancel() per annullare l'attività Callable associata. Esiste una versione sovraccaricata del metodo get() in cui è possibile specificare il tempo di attesa del risultato, è utile evitare che il thread corrente venga bloccato per un tempo più lungo. Esistono i metodi isDone() e isCancelled() per scoprire lo stato corrente dell'attività Callable associata.
Ecco un semplice esempio di attività Callable che restituisce il nome del thread che esegue l'attività dopo un secondo. Utilizziamo il framework Executor per eseguire 100 attività in parallelo e utilizzare Future per ottenere il risultato delle attività inoltrate.
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(1000);
//return the thread name executing this callable task
return Thread.currentThread().getName();
}
public static void main(String args[]){
//Get ExecutorService from Executors utility class, thread pool size is 10
ExecutorService executor = Executors.newFixedThreadPool(10);
//create a list to hold the Future object associated with Callable
List<Future<String>> list = new ArrayList<Future<String>>();
//Create MyCallable instance
Callable<String> callable = new MyCallable();
for(int i=0; i< 100; i++){
//submit Callable tasks to be executed by thread pool
Future<String> future = executor.submit(callable);
//add Future to the list, we can get return value using Future
list.add(future);
}
for(Future<String> fut : list){
try {
//print the return value of Future, notice the output delay in console
// because Future.get() waits for task to get completed
System.out.println(new Date()+ "::"+fut.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
//shut down the executor service now
executor.shutdown();
}
}
Dove come FutureTask è la base concreta dell'interfaccia futuri e fornisce elaborazione asincrona. Contiene i metodi per avviare e annullare un'attività e anche i metodi che possono restituire lo stato del FutureTask come se sia stato completato o annullato. Abbiamo bisogno di un oggetto callable per creare un'attività futura e quindi possiamo utilizzare Java Thread Pool Executor per elaborarli in modo asincrono.
Vediamo l'esempio di FutureTask con un semplice programma.
Poiché FutureTask richiede un oggetto callable, creeremo una semplice implementazione Callable.
public class MyCallable implements Callable<String> {
private long waitTime;
public MyCallable(int timeInMillis){
this.waitTime=timeInMillis;
}
@Override
public String call() throws Exception {
Thread.sleep(waitTime);
//return the thread name executing this callable task
return Thread.currentThread().getName();
}
}
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<String> futureTask1 = new FutureTask<String>(callable1);
FutureTask<String> futureTask2 = new FutureTask<String>(callable2);
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(futureTask1);
executor.execute(futureTask2);
while (true) {
try {
if(futureTask1.isDone() && futureTask2.isDone()){
System.out.println("Done");
//shut down executor service
executor.shutdown();
return;
}
if(!futureTask1.isDone()){
//wait indefinitely for future task to complete
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){
//do nothing
}
}
}
}
Il FutureTask implementa l'interfaccia Future, quindi quale è esattamente la differenza tra gli approcci in termini concreti? (esempio mostra un esempio) –
Task FutureTask = new FutureTask (richiamabile); \t \t // Implementazione 1 \t \t Fut Fut = service.submit (callable); \t \t // Implementazione 2. \t servizio \t.eseguire (task); perché dovremmo preferire l'implementazione 1 sull'implementazione 2. C'è qualche vantaggio? –
Non inserire il codice nei commenti, aggiornare la risposta. –