可運行問題

您已經熟悉Runnable接口和實現它的Thread類。讓我們回憶一下這個界面是什麼樣子的:


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

請注意,run方法的返回類型是void。但是,如果我們需要我們的線程以數字、字符串或其他對象的形式返回其工作的某些結果怎麼辦?那麼我們必須想出一個解決方法。像這樣:


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

此代碼有幾個缺點。例如,由於調用了join方法,主線程將在執行printByIndex方法時阻塞。

可調用接口

現在讓我們看看 Java 開箱即用的接口,它可以作為Runnable的替代品。這是可調用接口:


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

如您所見,就像Runnable一樣,它只有一種方法。此方法與run方法的目的相同——它包含將在並行線程中執行的代碼。至於區別,看一下返回值。現在它可以是您在實現接口時指定的任何類型:


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

另一個例子:


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

這裡還有一些有用的東西 — call方法可以拋出Exception。這意味著,與run方法不同,在call方法中我們不必處理方法內部發生的已檢查異常:


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密切合作的接口是FutureFuture表示異步(並行)計算的結果(調用方法返回的值)。它可以讓您檢查計算是否完成、等待計算完成、獲取計算結果等等。

未來接口的方法

  • boolean isDone() —如果此任務(計算)完成,則此方法返回true 。正常結束、異常結束或取消的任務被視為已完成。

  • V get() — 如有必要,此方法會阻塞調用它的線程,並在計算完成後返回計算結果。

  • V get(long timeout, TimeUnit unit) — 與前面的方法一樣,此方法阻塞調用它的線程,等待結果,但僅限於方法參數指定的時間。

  • boolean cancel(boolean mayInterruptIfRunning) — 此方法試圖停止任務的執行。如果任務還沒有開始運行,它永遠不會運行。如果任務正在進行中,則mayInterruptIfRunning參數確定是否會嘗試中斷執行任務的線程。調用cancel方法後, isDone方法將始終返回true

  • boolean isCancelled() —如果任務在正常完成之前被取消,則此方法返回true 。如果cancel方法之前被調用並返回true,該方法將始終返回true

使用 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