Visualizzazione post con etichetta java thread. Mostra tutti i post
Visualizzazione post con etichetta java thread. Mostra tutti i post

sabato 5 aprile 2014

Callable vs Runnable

In Java dalla versione 1.0 è presente l'interfaccia java.lang.Runnable, che come sappiamo deve essere implementata da quelle classi che si vuole eseguire come Thread separati.
Nell'interfaccia è dichiarato un solo metodo

void run()

Dalla versione 1.5 invece è stata creata una nuova interfaccia java.util.concurrent.Callable che per certi versi può essere vista come una estensione dell'interfaccia Runnable. Lo scopo dei progettisti è sempre quello di mantenere la retrocompatibilità, e questo è il motivo per cui non hanno modificato l'interfaccia Runnable ma ne hanno creata una nuova.

L'unico metodo definito dall'interfaccia Callable è:

V call() throws Exception

Il vantaggio di utilizzare Callable è che consente di ottenere indietro il risultato dell'esecuzione del task nel thread separato, cosa che tramite Runnable non è possibile.
In generale le differenze tra le 2 interfacce sono le seguenti:

  1. Un oggetto che implementa Callable non può essere utilizzato direttamente per costruire un Thread, mentre un oggetto che implementa Runnable può farlo;
  2. Un oggetto che implementa  Callable torna un risultato mentre un oggetto che implementa Runnable no;
  3. Un  oggetto che implementa Callable può rilanciare al chiamante una eccezione di tipo checked, mentre un oggetto che implementa Runnable può lanciare soltanto unchecked exceptions.
Vediamo un esempio di utilizzo di Callable.
Per utilizzare un oggetto che implementa un Callable dobbiamo necessariamente utilizzare un ExecutorService.

CALLABLE

import java.util.concurrent.Callable;
public class Fattoriale implements Callable<Long> {
 private long n;
 public Fattoriale(long n){
 this.n=n; 
 }
 @Override
 public Long call() throws Exception {
  
  if(n<=0) throw new Exception("Attenzione inserire un valore positivo!!");
  long f=1;
  for(long i=1;i<=n;i++){
   f*=i;
  }
  return f;
 }
}

EXECUTORSERVICE

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class FattorialeTest {
 public static void main(String[] args) throws Exception {
       long N=5;
       Callable<Long> c=new Fattoriale(N);
       ExecutorService es=Executors.newSingleThreadExecutor();
       Future<Long> f=es.submit(c);
       System.out.println(String.format("Il fattoriale di %d è %d",N,f.get()));
       es.shutdown();
 }
}


Si noti che l'executor service funziona anche con oggetti di tipo Runnable ma, poichè questi non tornano un risultato, il Future associato tornerà null se il processo è terminato correttamente.

domenica 19 gennaio 2014

Esempio utilizzo java.util.concurrent.Future, Callable e ExecutorService

L'interfaccia java.util.concurrent.Future schematizza un oggetto che dovrà tornare in futuro il risultato di una operazione asincrona.
L'interfaccia java.util.concurrent.Executor dichiara un solo metodo, void execute(Runnable)
L'interfaccia java.util.concurrent.ExecutorService estende l'interfaccia Executor aggiungendo funzionalità che consentono il termine dei thread e la produzione di oggetti Future.
L'interfaccia java.util.concurrent.Callable dichiara invece un solo metodo V call() throws Exception

Vediamo un esempio di una classe che implementa Callable e utilizza un ExecutorService per creare un oggetto Future passando il Callable.


package it.threads;
import java.util.concurrent.Callable;
public class OperazioneAsincrona implements Callable<String> {
 @Override
 public String call() throws Exception {
  System.out.println("Simulo lavorazione.....");
  Thread.sleep(3000);
  return "Lavorazione eseguita.....";
 }
}



Vediamo ora come richiamare il callable utilizzando ExecutorService e Future.

package it.threads;
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 OperazioneAsincronaTest {
 public static void main(String[] args) throws InterruptedException, ExecutionException {
       Callable<String> task=new OperazioneAsincrona();
       ExecutorService es=Executors.newSingleThreadExecutor();
       Future<String> f=es.submit(task);
       System.out.println(f.get());
       System.out.println("Esco dall'executor...");
       es.shutdown();
 }
}


Si può anche istanziare un ThradPool con l' ExecutorService, usando invece del newSingleThreadExecutor il metodo newFixedThreadPool che accetta come parametro in input un intero.