Проблемът Runnable

Вече сте запознати с интерфейса Runnable и класа Thread , който го реализира. Нека си припомним How изглежда този интерфейс:


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

Обърнете внимание, че типът връщане на метода run е void . Но Howво ще стане, ако имаме нужда нашата нишка да върне няHowъв резултат от работата си под формата на число, низ or няHowъв друг обект? Тогава трябва да измислим заобиколно решение. Нещо като това:


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

Нека изпълним следния основен метод:


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

Конзолата ще покаже:

Числото на Фибоначи 10: 34

Този code има няколко недостатъка. Например, в резултат на извикването на метода за присъединяване , основната нишка ще блокира, докато се изпълнява методът printByIndex .

Интерфейс с възможност за извикване

Сега нека да разгледаме интерфейса, който Java ни предоставя веднага, който може да се използва като алтернатива на Runnable . Това е Callable интерфейс:


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

Както можете да видите, точно като Runnable , той има само един метод. Този метод служи за същата цел като метода run — той съдържа codeа, който ще бъде изпълнен в паралелна нишка. Що се отнася до разликите, погледнете върнатата стойност. Сега може да бъде всеки тип, който посочите при внедряването на интерфейса:


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

Друг пример:


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

Ето още нещо полезно — методът за извикване може да генерира изключение . Това означава, че за разлика от метода run , в метода за повикване не трябва да обработваме проверените изключения, които възникват вътре в метода:


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;

	}

}

Бъдещ интерфейс

Друг интерфейс, който работи в тясно сътрудничество с Callable, е Future . Future представлява резултат от асинхронни (паралелни) изчисления (стойността, върната от метода за извикване ). Позволява ви да проверите дали изчисленията са напequalsи, да изчакате изчисленията да приключат, да получите резултата от изчисленията и др.

Методи на интерфейса на бъдещето

  • boolean isDone() — този метод връща true , ако тази задача (изчисление) е изпълнена. Задачи, които са приключor нормално, приключor с изключение or са бor отменени, се считат за изпълнени.

  • V get() — ако е необходимо, този метод блокира потока, който го е извикал, и връща резултата от изчисленията, когато са готови.

  • V get(long timeout, TimeUnit unit) — подобно на предишния метод, този метод блокира потока, който го е извикал, в очакване на резултата, но само за времето, определено от параметрите на метода.

  • boolean cancel(boolean mayInterruptIfRunning) — този метод се опитва да спре изпълнението на задачата. Ако задачата все още не е започнала да се изпълнява, тя никога няма да се изпълни. Ако задачата е била в ход, тогава параметърът mayInterruptIfRunning определя дали ще бъде напequals опит за прекъсване на нишката, изпълняваща задачата. След извикването на метода за отмяна , методът isDone винаги ще връща true .

  • boolean isCancelled() — този метод връща true , ако задачата е отменена, преди да завърши нормално. Методът винаги ще връща true, ако методът за отмяна е бил извикан преди това и е върнал true .

Пример за code, използващ Callable и 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;
 
	}
 
}

Нека изпълним следния основен метод:


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

Конзолата ще покаже:

Число на Фибоначи 16: 610
Число на Фибоначи 17: 987
Число на Фибоначи 18: 1597 Число на
Фибоначи 19: 2584
Число на Фибоначи 10: 34 Число на
Фибоначи 11: 55 Число на Фибоначи
12: 89 Число на Фибоначи 13
: 144
Число на Фибоначи 14: 233
Число на Фибоначи 15: 377