Java 基础教程

Java 面向对象

Java 高级教程

Java 笔记

Java Callable 和 Future


Java 中 Runnable 封装一个异步运行的任务,可以把它想象成为一个没有参数和返回值的异步方法。Callable 与 Runnable 类似,但是有返回值。Future 保存异步计算的结果。

Callable

Callable 接口是一个参数化的类型,只有一个方法 call。

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

类型参数是返回值的类型。例如,Callable<Integer> 表示一个最终返回 Integer 对象的异步计算。

Future

Future 保存异步计算的结果。可以启动一个计算,将 Future 对象交给某个线程,然后忘掉它。Future 对象的所有者在结果计算好之后就可以获得它。

Future 接口具有如下方法:

public interface Future<V> {

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();
}

第一个 get 方法的调用被阻塞,直到计算完成。如果在计算完成之前,第二个方法的调用超时,则拋出一个 TimeoutException 异常。如果运行该计算的线程被中断,两个方法都将拋出 InterruptedException。如果计算已经完成,那么 get 方法立即返回。

如果计算还在进行,isDone 方法返回 false;如果完成了,则返回 true。

可以用 cancel 方法取消该计算。如果计算还没有开始,它被取消且不再开始。如果计算处于运行之中,那么如果 maylnterrupt 参数为 true,它就被中断。

FutureTask

FutureTask 同时实现了 Runnable 接口和 Future 接口,所以它既可以作为 Runnable 被线程执行,又可以作为 Future 得到 Callable 的返回值,FutureTask 包装器是一种非常便利的机制。

FutureTask 提供了 2 个构造器:

public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

示例

import java.util.concurrent.*;


public class KnowledgeDict {

    public static void main(String[] args) {

        ExecutorService executor = Executors.newCachedThreadPool();
        Task task = new Task();
        FutureTask<Integer> futureTask = new FutureTask<>(task);
        executor.submit(futureTask);
        executor.shutdown();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("主线程在执行任务");

        try {
            System.out.println("task 运行结果:" + futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println("所有任务执行完毕");
    }

    static class Task implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            System.out.println("子线程在进行计算");
            Thread.sleep(3000);
            int sum = 0;
            for (int i = 0; i < 100; i++)
                sum += i;
            return sum;
        }
    }
}