Fork me on GitHub

浅析java并发包(五):Callable、Future和FutureTask

前言

创建一个线程,最熟悉的做法是集成Thread类或者实现Runnable接口重新run()。 但是这两种创建方式在一些场景比如想要获取线程的执行结果时却不那么好用。J.U.C满足了这种需求。 今天说一下不得不知的CallableFutureFutureTask

Callable

比对Runnable接口与Callable接口:

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

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

可以看到,Callable接口代表一种能返回结果并可能引发异常的任务。 实现者只要实现call()并返回任务结果即可。 Callable接口类似于Runnable,但是,Runnable不返回结果,也不能抛出被检查的异常。

一个好的事实是,无论是Callable还是Runnable,都可以很好的与Executor框架结合使用:

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);

Future

future,单从语义上就知道代表未来,实际上也是如此。 先看接口定义:

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Future提供了5个方法:

  • cancel(boolean mayInterruptIfRunning):尝试取消执行此任务。
  • isCancelled():如果此任务在正常完成之前被取消,则返回 true 。
  • isDone():如果任务已完成返回 true。
  • get():一直阻塞直至获取执行结果。
  • get(long timeout, TimeUnit unit):在指定时间内获取执行结果,获取不到抛出TimeoutException

FutureTask

FutureTask实现了RunnableFuture接口,看下RunnableFuture接口:

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

可以看到它即实现了Runnable接口,也实现了Future接口,因此FutureTask可以作为Runnable的实现, 也可以作为Future来获取Callable的返回。看其构造器便知如何选择:

public FutureTask(Callable<V> callable) {
}
public FutureTask(Runnable runnable, V result) {
}

推荐的使用方式

开发中建议使用线程池+Callable返回Future的方式来获取异步结果,最好给予超时时间:

ExecutorService es = Executors.newSingleThreadExecutor();
Future<Integer> future = es.submit(() -> {
    try {
        System.out.println("开始执行计算任务...");
        Thread.sleep(5000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return 100;
});
es.shutdown();
//        Integer integer = future.get();
//        System.out.println("get() 获取异步任务结果:" + integer);
Integer integer;
try {
    integer = future.get(2, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    System.out.println("调用超时,返回错误结果");
    integer = -1;
}
System.out.println("get(timeout) 获取异步任务结果:" + integer);
}

返回的结果值:

开始执行计算任务...
调用超时,返回错误结果
get(timeout) 获取异步任务结果:-1
-------------本文结束,感谢您的阅读-------------
贵在坚持,如果您觉得本文还不错,不妨打赏一下~
0%