Det körbara problemet

Du är redan bekant med Runnable- gränssnittet och klassen Thread som implementerar det. Låt oss komma ihåg hur det här gränssnittet ser ut:


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

Observera att körmetodens returtyp är ogiltig . Men vad händer om vi behöver vår tråd för att returnera något resultat av dess arbete i form av ett nummer, en sträng eller något annat objekt? Då måste vi komma på en lösning. Något som det här:


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

Låt oss köra följande huvudmetod :


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

Konsolen kommer att visa:

Fibonacci nummer 10:34

Denna kod har flera nackdelar. Till exempel, som ett resultat av anropet till join -metoden, kommer huvudtråden att blockeras medan printByIndex -metoden exekveras.

Anropsbart gränssnitt

Låt oss nu titta på gränssnittet som Java ger oss ur lådan, som kan användas som ett alternativ till Runnable . Det här är det anropbara gränssnittet:


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

Som du kan se, precis som Runnable , har den bara en metod. Denna metod tjänar samma syfte som körmetoden - den innehåller koden som kommer att exekveras i en parallell tråd. När det gäller skillnader, ta en titt på returvärdet. Nu kan det vara vilken typ som helst som du anger när du implementerar gränssnittet:


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

Ett annat exempel:


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

Här är något annat användbart — anropsmetoden kan skapa ett undantag . Det betyder att vi, till skillnad från körmetoden , i anropsmetoden inte behöver hantera de markerade undantagen som förekommer i metoden:


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;

	}

}

Framtida gränssnitt

Ett annat gränssnitt som arbetar nära med Callable är Future . Framtid representerar resultatet av asynkrona (parallella) beräkningar (det värde som returneras av anropsmetoden ) . Den låter dig kontrollera om beräkningarna är gjorda, vänta tills beräkningarna är klara, få resultatet av beräkningarna och mer.

Metoder för framtidens gränssnitt

  • boolean isDone() — den här metoden returnerar true om denna uppgift (beräkning) görs. Uppgifter som slutade normalt, slutade med ett undantag eller avbröts anses vara gjorda.

  • V get() — om nödvändigt blockerar denna metod tråden som anropade den och returnerar resultatet av beräkningarna när de är klara.

  • V get(long timeout, TimeUnit unit) — liksom den tidigare metoden, blockerar denna metod tråden som anropade den och väntar på resultatet, men bara under den tid som anges av metodparametrarna.

  • boolean cancel(boolean mayInterruptIfRunning) — den här metoden försöker stoppa exekveringen av uppgiften. Om uppgiften inte har börjat köras än kommer den aldrig att köras. Om uppgiften pågick bestämmer parametern mayInterruptIfRunning om ett försök kommer att göras att avbryta tråden som utför uppgiften. Efter att avbrytningsmetoden anropats kommer isDone- metoden alltid att returnera true .

  • boolean isCancelled() — den här metoden returnerar true om uppgiften avbryts innan den avslutas normalt. Metoden kommer alltid att returnera true om avbrytningsmetoden tidigare anropats och returnerat true .

Exempel på kod som använder Callable och 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;
 
	}
 
}

Låt oss köra följande huvudmetod :


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

Konsolen kommer att visa:

Fibonacci nummer 16: 610
Fibonacci nummer 17: 987
Fibonacci nummer 18: 1597
Fibonacci nummer 19: 2584
Fibonacci nummer 10: 34
Fibonacci nummer 11: 55 Fibonacci
nummer 12
: 89
Fibonacci nummer 134ci nummer: 341ci
nummer 1341 15:377