天天看点

Java并发编程之Callable、Future和FutureTask概念 *

概述

在实际开发中Runnable等等线程的run方法是没有返回值结果的,如果我需要拿到线程执行完的结果,就需要Callable方法了.

Callable概念

Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法,只不过这个方法叫做call(),这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}      

Callable一般情况下是配合ExecutorService来使用的,在ExecutorService接口中声明了若干个submit方法的重载版本.

ExecutorService下submit的三个重载方法:
<T> Future<T> submit(Callable<T> task);
submit提交一个实现Callable接口的任务,并且返回封装了异步计算结果的Future。
<T> Future<T> submit(Runnable task, T result);
submit提交一个实现Runnable接口的任务,并且指定了在调用Future的get方法时返回的result对象。
Future<?> submit(Runnable task);
submit提交一个实现Runnable接口的任务,并且返回封装了异步计算结果的Future。      

因此只需要创建好实现Callable接口或者Runnable接口,通过ExecutorService的submit方法提交给线程池执行即可.

实现Callable的任务执行后可返回值,而Runnable的任务是不能返回值(是void)。

Future概念

Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

Future的get方法

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

它声明这样的五个方法:

cancel方法用来取消任务,当FutureTask处于未启动状态时,执行FutureTask.cancel()方法将导致此任务永远不会被执行;当FutureTask处于已启动状态时,执行FutureTask.cancel(true)方法将以中断执行此任务线程的方式来试图停止任务;当FutureTask处于已启动状态时,执行FutureTask.cancel(false)方法将不会对正在执行此任务的线程产生影响(让正在执行的任务运行完成);当FutureTask处于已完成状态时,执行FutureTask.cancel(…)方法将返回false。

isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。

isDone方法表示任务是否已经完成,若任务完成,则返回true;

get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;

get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。同时会抛异常出来.

也就是说Future提供了三种功能:

判断任务是否完成;

能够中断任务;

能够获取任务执行结果。

因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。

FutureTask概念

因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。

Future是一个接口

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}      

FutureTask实现了RunnableFuture,而RunnableFuture继承了 Future

public class FutureTask<V> implements RunnableFuture<V> {      

FutureTask类实现了RunnableFuture接口,RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

Java并发编程之Callable、Future和FutureTask概念 *

因此我们通过一个线程运行Callable,但是Thread不支持构造方法中传递Callable的实例,所以我们需要通过FutureTask把一个Callable包装成Runnable,然后再通过这个FutureTask拿到Callable运行后的返回值。

要new一个FutureTask的实例,有两种方法

Java并发编程之Callable、Future和FutureTask概念 *
/* Task实现了Callable接口*/
FutureTask futureTask = new FutureTask<>(new Task());
new Thread(futureTask).start();      

FutureTask三种状态

1)未启动。FutureTask.run()方法还没有被执行之前,FutureTask处于未启动状态。当创建一个FutureTask,且没有执行FutureTask.run()方法之前,这个FutureTask处于未启动状态。

2)已启动。FutureTask.run()方法被执行的过程中,FutureTask处于已启动状态。

3)已完成。FutureTask.run()方法执行完后正常结束,或被取消(FutureTask.cancel(…)),或执行FutureTask.run()方法时抛出异常而异常结束,FutureTask处于已完成状态。

当FutureTask处于未启动或已启动状态时,执行FutureTask.get()方法将导致调用线程阻塞;

当FutureTask处于已完成状态时,执行FutureTask.get()方法将导致调用线程立即返回结果或抛出异常。

当FutureTask处于未启动状态时,执行FutureTask.cancel()方法将导致此任务永远不会被执行;当FutureTask处于已启动状态时,执行FutureTask.cancel(true)方法将以中断执行此任务线程的方式来试图停止任务;当FutureTask处于已启动状态时,执行FutureTask.cancel(false)方法将不会对正在执行此任务的线程产生影响(让正在执行的任务运行完成);当FutureTask处于已完

成状态时,执行FutureTask.cancel(…)方法将返回false。

三者关系总结

线程池有sublime方法,可以传入Callable类型的值,会返回一个Future的子类FutureTask, 也就是这个关系.

实现Runnable接口和实现Callable接口的区别

1、Runnable是自从java1.1就有了,而Callable是1.5之后才加上去的。

2、Callable规定的方法是call(),Runnable规定的方法是run()。

3、Callable的任务执行后可返回值,而Runnable的任务是不能返回值(是void)。

4、call方法可以抛出异常,run方法不可以。