Le problème Runnable

Vous connaissez déjà l' interface Runnable et la classe Thread qui l'implémente. Rappelons à quoi ressemble cette interface :


public interface Runnable {
	public abstract void run();
}

Notez que le type de retour de la méthode run est void . Mais que se passe-t-il si nous avons besoin que notre thread renvoie un résultat de son travail sous la forme d'un nombre, d'une chaîne ou d'un autre objet ? Ensuite, nous devons trouver une solution de contournement. Quelque chose comme ça:


public class Fibonacci implements Runnable {
 
 
 
	private final int index;
 
	private int result;
 
 
 
	public Fibonacci(int index) {
 
    		this.index = index;
 
	}
 
 
 
	@Override
 
	public void run() {
 
    		int first = 0;
 
    		int second = 1;
 
    		if (index == 1) {
 
        			result = first;
 
    		} else if (index == 2) {
 
        			result = second;
 
    		} else {
 
        			for (int i = 0; i < index - 2; i++) {
 
            				int temp = second;
 
            				second += first;
 
            				first = temp;
 
        			}
 
 
 
            			result = second;
 
    		}
 
	}
 
 
 
	public static void printByIndex(int index) throws InterruptedException {
 
    		Fibonacci fibonacci = new Fibonacci(index);
 
    		Thread thread = new Thread(fibonacci);
 
    		thread.start();
 
    		thread.join();
 
    		System.out.println("Fibonacci number " + index + ": " + fibonacci.result);
 
	}
 
}

Exécutons la méthode principale suivante :


	public static void main(String[] args) throws Exception {
    		Fibonacci.printByIndex(10);
	}

La console affichera :

Nombre de Fibonacci 10 : 34

Ce code présente plusieurs inconvénients. Par exemple, à la suite de l'appel à la méthode join , le thread principal se bloquera pendant l'exécution de la méthode printByIndex .

Interface appelable

Examinons maintenant l'interface prête à l'emploi de Java, qui peut être utilisée comme alternative à Runnable . Voici l' interface appelable :


public interface Callable<V> {
 
	V call() throws Exception;
 
}

Comme vous pouvez le voir, tout comme Runnable , il n'a qu'une seule méthode. Cette méthode a le même objectif que la méthode run — elle contient le code qui sera exécuté dans un thread parallèle. En ce qui concerne les différences, jetez un œil à la valeur de retour. Désormais, il peut s'agir de n'importe quel type que vous spécifiez lors de l'implémentation de l'interface :


public class CurrentDate implements Callable<Long> {
 
	@Override
 
	public Long call() {
 
    		return new Date().getTime();
 
	}
 
}

Un autre exemple:


Callable<String> task = () -> {
 
	Thread.sleep(100);
 
	return "Done";
 
};

Voici quelque chose d'autre d'utile — la méthode call peut lancer une Exception . Cela signifie que, contrairement à la méthode run , dans la méthode call , nous n'avons pas à gérer les exceptions vérifiées qui se produisent à l'intérieur de la méthode :


public class Sleep implements Runnable {

	@Override

	public void run() {

    	    try {

        	        Thread.sleep(1000);

    	    } catch (InterruptedException ignored) {

    	    }

	}

}

public class Sleep implements Callable {

	@Override

	public Object call() throws InterruptedException {

    	    Thread.sleep(1000);

    	    return null;

	}

}

Interface future

Une autre interface qui travaille en étroite collaboration avec Callable est Future . Future représente le résultat de calculs asynchrones (parallèles) (la valeur renvoyée par la méthode d'appel ). Il vous permet de vérifier si les calculs sont effectués, d'attendre la fin des calculs, d'obtenir le résultat des calculs, etc.

Interface Méthodes du Futur

  • boolean isDone() — cette méthode renvoie true si cette tâche (calcul) est terminée. Les tâches qui se sont terminées normalement, se sont terminées avec une exception ou ont été annulées sont considérées comme terminées.

  • V get() — si nécessaire, cette méthode bloque le thread qui l'a appelée et renvoie le résultat des calculs lorsqu'ils sont terminés.

  • V get(long timeout, TimeUnit unit) — comme la méthode précédente, cette méthode bloque le thread qui l'a appelée, attendant le résultat, mais seulement pendant le temps spécifié par les paramètres de la méthode.

  • boolean cancel(boolean mayInterruptIfRunning) — cette méthode essaie d'arrêter l'exécution de la tâche. Si la tâche n'a pas encore commencé à s'exécuter, elle ne s'exécutera jamais. Si la tâche était en cours, le paramètre mayInterruptIfRunning détermine si une tentative sera faite pour interrompre le thread exécutant la tâche. Une fois la méthode d'annulation appelée, la méthode isDone renverra toujours true .

  • boolean isCancelled() — cette méthode renvoie true si la tâche est annulée avant qu'elle ne se termine normalement. La méthode renverra toujours true si la méthode d'annulation a été précédemment appelée et a renvoyé true .

Exemple de code utilisant Callable et Future


import java.util.HashMap;
 
import java.util.Map;
 
import java.util.concurrent.*;
 
 
 
public class Fibonacci implements Callable<Integer> {
 
 
 
	private final int index;
 
 
 
	public Fibonacci(int index) {
 
    		this.index = index;
 
	}
 
 
 
	@Override
 
	public Integer call() {
 
    		int first = 0;
 
    		int second = 1;
 
    		if (index == 1) {
 
        			return first;
 
    		} else if (index == 2) {
 
        			return second;
 
    		} else {
 
        		for (int i = 0; i < index - 2; i++) {
 
            			int temp = second;
 
            			second += first;
 
            			first = temp;
 
        		}
 
 
 
        			return second;
 
    		}
 
	}
 
 
 
	public static Future<Integer> calculateAsync(int index) throws Exception {
 
    		Fibonacci fibonacci = new Fibonacci(index);
 
 
 
    		// The future object will represent the result of running the fibonacci task.
 
    		FutureTask<Integer> future = new FutureTask<>(fibonacci);
 
 
 
    		// Because the FutureTask class implements both the Future interface and the Runnable interface,
 
	 	// you can pass instances of it to the Thread constructor
 
    		Thread thread = new Thread(future);
 
    		thread.start();
 
 
 
    		return future;
 
	}
 
}

Exécutons la méthode principale suivante :


	public static void main(String[] args) throws Exception {
    		Map<Integer, Future<Integer>> tasks = new HashMap<>();
    		for (int i = 10; i < 20; i++) {
        			tasks.put(i, Fibonacci.calculateAsync(i));
    		}
 
    		for (Map.Entry<Integer, Future<Integer>> entry : tasks.entrySet()) {
        			Future<Integer> task = entry.getValue();
        			int index = entry.getKey();
        			int result;
        			// Check whether the task is done
        			if (task.isDone()) {
            				// Get the result of the calculations
            				result = task.get();
        			} else {
            				try {
                				// Wait another 100 milliseconds for the result of the calculations
                				result = task.get(100, TimeUnit.MILLISECONDS);
            				} catch (TimeoutException e) {
                				// Interrupt the task
                				task.cancel(true);
                				System.out.println("Fibonacci number " + index + " could not be calculated in the allotted time.");
                				return;
            				}
        			}
        			System.out.println("Fibonacci number " + index + ": " + result);
    		}
	}

La console affichera :

Nombre de Fibonacci 16 : 610
Nombre de Fibonacci 17 : 987
Nombre de Fibonacci 18 : 1597 Nombre de
Fibonacci 19 : 2584 Nombre de
Fibonacci 10 : 34 Nombre de
Fibonacci 11 : 55 Nombre de Fibonacci
12 : 89 Nombre de Fibonacci 13
: 144
Nombre de Fibonacci 14 : 233
Nombre de Fibonacci 15 : 377