可运行问题

您已经熟悉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