Problema Runnable

Sunteți deja familiarizat cu interfața Runnable și cu clasa Thread care o implementează. Să ne amintim cum arată această interfață:


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

Rețineți că tipul de returnare al metodei run este void . Dar dacă avem nevoie de firul nostru pentru a returna un rezultat al muncii sale sub forma unui număr, a unui șir sau a unui alt obiect? Atunci trebuie să găsim o soluție. Ceva de genul:


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);
 
	}
 
}

Să rulăm următoarea metodă principală :


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

Consola va afișa:

Numărul Fibonacci 10: 34

Acest cod are mai multe dezavantaje. De exemplu, ca rezultat al apelului la metoda join , firul principal se va bloca în timp ce metoda printByIndex este executată.

Interfață apelabilă

Acum să ne uităm la interfața pe care ne-o oferă Java din cutie, care poate fi folosită ca alternativă la Runnable . Aceasta este interfața apelabilă :


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

După cum puteți vedea, la fel ca Runnable , are o singură metodă. Această metodă are același scop ca metoda run - conține codul care va fi executat într-un fir paralel. În ceea ce privește diferențele, aruncați o privire la valoarea returnată. Acum poate fi orice tip pe care îl specificați când implementați interfața:


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

Alt exemplu:


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

Iată încă ceva util - metoda de apel poate lansa o excepție . Asta înseamnă că, spre deosebire de metoda run , în metoda call nu trebuie să gestionăm excepțiile verificate care apar în interiorul metodei:


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;

	}

}

Interfață viitoare

O altă interfață care lucrează îndeaproape cu Callable este Future . Viitorul reprezintă rezultatul calculelor asincrone (paralele) (valoarea returnată de metoda call ). Vă permite să verificați dacă calculele sunt efectuate, să așteptați ca calculele să se termine, să obțineți rezultatul calculelor și multe altele.

Interfața Metodelor Viitorului

  • boolean isDone() — această metodă returnează adevărat dacă această sarcină (calcul) este efectuată. Activitățile care s-au încheiat normal, s-au încheiat cu o excepție sau au fost anulate sunt considerate finalizate.

  • V get() — dacă este necesar, această metodă blochează firul de execuție care a apelat-o și returnează rezultatul calculelor când sunt terminate.

  • V get(long timeout, TimeUnit unit) — ca și metoda anterioară, această metodă blochează firul care a apelat-o, așteptând rezultatul, dar numai pentru timpul specificat de parametrii metodei.

  • boolean cancel(boolean mayInterruptIfRunning) — această metodă încearcă să oprească execuția sarcinii. Dacă sarcina nu a început încă să ruleze, nu va rula niciodată. Dacă sarcina era în desfășurare, atunci parametrul mayInterruptIfRunning determină dacă se va încerca întreruperea firului de execuție a sarcinii. După ce metoda cancel este apelată, metoda isDone va returna întotdeauna true .

  • boolean isCancelled() — această metodă returnează true dacă sarcina este anulată înainte de a se termina normal. Metoda va returna întotdeauna true dacă metoda cancel a fost apelată anterior și a returnat true .

Exemplu de cod folosind Callable și 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;
 
	}
 
}

Să rulăm următoarea metodă principală :


	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);
    		}
	}

Consola va afișa:

Numărul Fibonacci 16: 610
Numărul Fibonacci 17: 987 Numărul
Fibonacci 18: 1597 Numărul
Fibonacci 19: 2584 Numărul
Fibonacci 10: 34 Numărul Fibonacci
11: 55 Numărul Fibonacci 12:
89 Numărul Fibonacci
13: 1414
Numărul Fibonacci 13: 1414
Numărul Fibonacci 14:3235