Java Callable e Future sono molto utilizzati nella programmazione multithread. Negli ultimi post, abbiamo imparato molto sui thread di java, ma a volte desideriamo che un thread possa restituire qualche valore che possiamo utilizzare. Java 5 ha introdotto l’interfaccia java.util.concurrent.Callable nel pacchetto concurrency che è simile all’interfaccia Runnable ma può restituire qualsiasi Object e può lanciare Eccezioni.
Java Callable
L’interfaccia Java Callable utilizza Generic per definire il tipo di ritorno dell’Object. La classe Executors fornisce metodi utili per eseguire Java Callable in un pool di thread. Poiché i compiti callable vengono eseguiti in parallelo, dobbiamo attendere l’Object restituito.
Java Future
I compiti Java Callable restituiscono un oggetto java.util.concurrent.Future. Utilizzando l’oggetto Java Future, possiamo scoprire lo stato del compito Callable e ottenere l’oggetto restituito. Fornisce il metodo get() che può attendere il completamento del Callable e quindi restituire il risultato. Java Future fornisce il metodo cancel() per annullare il compito Callable associato. Esiste una versione sovraccaricata del metodo get() in cui possiamo specificare il tempo di attesa per il risultato, è utile per evitare che il thread corrente venga bloccato per un tempo più lungo. Ci sono i metodi isDone() e isCancelled() per scoprire lo stato attuale del compito Callable associato. Ecco un semplice esempio di compito Java Callable che restituisce il nome del thread che esegue il compito dopo un secondo. Stiamo utilizzando il framework Executor per eseguire 100 compiti in parallelo e utilizziamo Java Future per ottenere il risultato dei compiti inviati.
package com.journaldev.threads;
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 {
@Override
public String call() throws Exception {
Thread.sleep(1000);
//restituisci il nome del thread che esegue questo compito chiamabile
return Thread.currentThread().getName();
}
public static void main(String args[]){
//Ottieni ExecutorService dalla classe di utilità Executors, la dimensione del pool di thread è 10
ExecutorService executor = Executors.newFixedThreadPool(10);
//crea una lista per contenere l'oggetto Future associato a Callable
List> list = new ArrayList>();
//Crea un'istanza di MyCallable
Callable callable = new MyCallable();
for(int i=0; i< 100; i++){
//sottoponi i compiti Callable da eseguire dal pool di thread
Future future = executor.submit(callable);
//aggiungi Future alla lista, possiamo ottenere il valore di ritorno usando Future
list.add(future);
}
for(Future fut : list){
try {
//stampa il valore di ritorno di Future, notare il ritardo di output nella console
//perché Future.get() attende che il compito venga completato
System.out.println(new Date()+ "::"+fut.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
//spegni ora il servizio executor
executor.shutdown();
}
}
Dopo aver eseguito il programma sopra, noterai il ritardo nell’output perché il metodo get() di Java Future attende che il compito chiamabile di Java venga completato. Notare anche che ci sono solo 10 thread che eseguono questi compiti. Ecco uno snippet dell’output del programma sopra.
Mon Dec 31 20:40:15 PST 2012::pool-1-thread-1
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-2
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-3
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-4
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-5
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-6
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-7
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-8
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-9
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-10
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-2
...
Suggerimento: E se volessimo sovrascrivere alcuni dei metodi dell’interfaccia Java Future, ad esempio sovrascrivendo il metodo get()
per terminare dopo un certo tempo predefinito anziché attendere indefinitamente, in questo caso la classe Java FutureTask risulta utile che è l’implementazione di base dell’interfaccia Future. Dai un’occhiata all’esempio di Java FutureTask per saperne di più su questa classe.
Source:
https://www.digitalocean.com/community/tutorials/java-callable-future-example