Metodi asincroni in Spring
Vitalij Zadneprovskij
ROME 24-25 MARCH 2017
JUG Roma e Code Garden
I processi del sistema operativo
●
Istanza di
programma in
esecuzione
●
Comunicazione
limitata
● JVM è un processo,
di solito
Thread nel sistema operativo
●
Eseguono istruzioni
in modo
concorrente
●
Condividono dati
Fonte immagine: wikipedia.org
Thread in Java
●
Un main thread
può lanciarne altri
● Istanza classe
Thread
● Ogni thread ha una
sua pila di
chiamate
Fonte immagine: http://blog.takipi.com
Ciclo di vita di un thread di Java
Fonte immagine: https://www.tutorialspoint.com
Lancio di un thread
public class HelloRunnable implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
    }
    public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start();
    }
}
Thread pool
●
Allocare e liberare memoria degrada le
prestazioni
● È meglio fare riuso di thread
● Un thread usato torna nel pool
● Numero di thread nel pool può essere
fisso o variabile
Interfaccia Executor
●
Astrazione per la gestione dei thread pool
● Unico metodo:
void execute(Runnable command);
Che ha un funzionamento simile a:
(new Thread(runnable)).start();
● Astrazione sulla gestione del thread pool
Esempio didattico di Executor
class ThreadPerTaskExecutor implements 
Executor {
   public void execute(Runnable r) {
     new Thread(r).start();
   }
 }
Executor in pratica
●
Conviene delegare la gestione dei pool di
thread al container
● Oppure usare ThreadPoolExecutor e
ScheduledThreadPoolExecutor
Interfaccia Callable
public interface Callable<V> {
    V call() throws Exception;
}
●
Restituisce un valore del tipo che
vogliamo noi
● Può lanciare eccezioni al chiamante
Future
Fonte immagine: boardgamegeek.com
Future
●
Rappresenta una computazione
● Può essere in corso, eseguita o annullata
● Chiamando il metodo get() metto in
waiting il thread chiamante
Interfaccia Future
public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, 
ExecutionException;
    V get(long timeout, TimeUnit unit) throws 
InterruptedException, ExecutionException, 
TimeoutException;
}
Si può fare di meglio
Le callback
●
Quando il metodo
asincrono termina,
ne chiama un altro
●
Metodi distinti in
caso di successo o
errore
Lana Turner
ListenableFuture di Spring Core
public interface ListenableFuture<T> extends 
Future<T> {
    void addCallback(ListenableFutureCallback<? 
super T> var1);
    void addCallback(SuccessCallback<? super T> 
var1, FailureCallback var2);
}
E se la callback dovesse
chiamare un’altra callback?
●
Può capitare di dover
fare varie chiamate
consecutive
●
Se non si sta attenti,
si finisce per scrivere
codice difficile da
modificare
Esempio in Java
ListenableFuture<String> listenable = 
asyncService.listenableMethod();
        listenable.addCallback(new 
ListenableFutureCallback<String>(){
            public void onSuccess(String s) {
                try {
                    ListenableFuture lf = 
asyncService.listenableMethod();
                    lf.addCallback(new ListenableFutureCallback() {
                        public void onFailure(Throwable throwable) {
                            try {
                                ListenableFuture lf2 = 
asyncService.listenableMethod();
                            } catch (InterruptedException e) 
{ logger.error(e.getLocalizedMessage(), e); }
                        }
                        public void onSuccess(Object o) { }
                    });
                } catch (InterruptedException e) 
{ logger.error(e.getLocalizedMessage(), e); }
            }
            public void onFailure(Throwable throwable) {}
        });
L’inferno delle callback
John Martin “Le Pandemonium” Louvre
Facciamoci aiutare da Java 8
●
Ho una classe MiaClasse
che ha un metodo
mioMetodo()
●
Posso passare questo
metodo come
argomento:
MiaClasse::mioMetodo
CompletableFuture di Java 8
●
Passiamo i metodi come argomenti
● Aggiungere callback: thenApply()
● Gestire errori: exceptionally()
● Ultimo metodo: thenAccept()
Esempio di CompletableFuture
CompletableFuture<String> completableFuture 
= asyncService.completableMethod();
completableFuture = 
completableFuture.exceptionally(this::onExce
ption);
completableFuture = 
completableFuture.thenApply(this::intermedia
teMethod);
completableFuture.thenAccept(this::lastMetho
d);
Altro esempio
CompletableFuture.supplyAsync(this::findReceiver)
                     .thenApply(this::sendMsg)
                     .thenAccept(this::notify);
Arriviamo a Spring
Sandro Botticelli, “Primavera”
Configurazione generale
●
Da XML, dopo aver aggiunto il namespace
XML “task”:
<task:annotation­driven />
● Annotation per Spring Boot:
@EnableAsync
Configurazione TaskExecutor
● Di default viene usato il
SimpleAsyncTaskExecutor che non riusa i
thread
● Per usare il ThreadPoolTaskExecutor:
<bean id="taskExecutor" 
class="org.springframework.scheduling.concurrent.Thread
PoolTaskExecutor">
        <property name="corePoolSize" value="5" />
        <property name="maxPoolSize" value="10" />
        <property name="queueCapacity" value="25" />
    </bean>
Valori di ritorno ammessi
● Future
● ListenableFuture
● CompletableFuture (richiede Java 8)
Esempio di Future
    @Async
    public Future<String> futureMethod(){
        Future<String> future = null;
        try {
            Thread.sleep(1000L);
            future = AsyncResult.forValue("OK");
        } catch (InterruptedException e) {
            logger.error(e.getLocalizedMessage(), e);
            future = 
AsyncResult.forExecutionException(e);
        }
        return future;
    }
Esempio di ListenableFuture
    @Async
    public ListenableFuture listenableMethod() throws 
InterruptedException {
        ListenableFuture<String> listenableFuture = null;
        try {
            Thread.sleep(1000L);
            listenableFuture = AsyncResult.forValue("OK");
        } catch (InterruptedException e) {
            logger.error(e.getLocalizedMessage(), e);
            listenableFuture = 
AsyncResult.forExecutionException(e);
        }
        return listenableFuture;
    }
Esempio di CompletableFuture
    @Async
    public CompletableFuture<String> completableMethod() 
throws InterruptedException{
        CompletableFuture<String> completableFuture = new 
CompletableFuture<String>();
        try {
            Thread.sleep(1000L);
            completableFuture.complete("OK");
        } catch (InterruptedException e) {
            logger.error(e.getLocalizedMessage(), e);
            completableFuture.completeExceptionally(e);
        }
        return completableFuture;
    }
Lato Controller
●
Nei vari controller, va restituito un
DeferredResult
● Il DeferredResult deve essere valorizzato
nella callback
● Può contenere il valore da restituire,
oppure un ModelAndView
Silvia Bottini, “Cry”
Esempio Spring Data JPA
interface EmployeeRepository extends 
JpaRepository<Employee, Long> {
    @Async
    Future<List<Employee>> findDistinctByFirstName(String 
firstName);
    @Async
    CompletableFuture<Employee> 
findFirstByFirstNameIgnoreCase(String firstName);
    @Async
    ListenableFuture<Employee> findFirstByLastName(String 
lastName);
}
Metodi asincroni in spring

Metodi asincroni in spring