1.创建线程任务方式一:Runnable
Runnable#run() 实现线程逻辑, 无返回值
public interface Runnable {
public abstract void run();
}
2.创建线程任务方式一:Callable
2.1 Callable
Callable#call() 实现线程逻辑,有返回值(V)
public interface Callable<V> {
V call() throws Exception;
}
2.2 Future
Callabe 不能单独使用,需要 Future 用来控制 Callable 执行,获取 Callable 执行结果
public interface Future<V> {
// 等待结果返回
// 如果任务被取消了,抛 CancellationException 异常
// 如果等待过程中被打断了,抛 InterruptedException 异常
V get() throws InterruptedException, ExecutionException;
// 等待,但是带有超时时间的,如果超时时间外仍然没有响应,抛 TimeoutException 异常
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
// 如果任务已经成功了,或已经取消了,是无法再取消的,会直接返回取消成功(true)
// 如果任务还没有开始进行时,发起取消,是可以取消成功的。
// 如果取消时,任务已经在运行了,mayInterruptIfRunning 为 true 的话,就可以打断运行中的线程
// mayInterruptIfRunning 为 false,表示不能打断直接返回
boolean cancel(boolean mayInterruptIfRunning);
// 返回线程是否已经被取消了,true 表示已经被取消了
// 如果线程已经运行结束了,isCancelled 和 isDone 返回的都是 true
boolean isCancelled();
// 线程是否已经运行结束了
boolean isDone();
}
1)get 方法主要作用是得到 Callable 异步任务执行的结果,无参 get 会一直等待任务执行完成之后才返回,有参 get 方法可以设定固定的时间,在设定的时间内,如果任务还没有执行成功,直接返回异常,在实际工作中,建议多多使用 get 有参方法,少用 get 无参方法,防止任务执行过慢时,多数线程都在等待,造成线程耗尽的问题。
2)cancel 方法主要用来取消任务,如果任务还没有执行,是可以取消的,如果任务已经在执行过程中了,你可以选择不取消,或者直接打断执行中的任务
那么问题来了…
=> 两个问题
问题一:Callable 与 Future都是接口,怎么实现通过 Future 控制 Callable 呢?
答:可以创建一个中间类实现 Future接口,然后将 Callable 实例组合进来,最后通过 Future 接口中的方法实现控制。
问题二:另外,这里还要考虑一个问题,Runnable 和 Callable 都可以表示线程要执行的任务,那么这两个接口如何在不改变原有关系的基础上互相转化?
答:首先,Runnable 与 Callable 肯定不能通过 extends 实现,因为直接继承就是 is-a 的关系,而这俩显然不是。那么还有一个办法,引入一个中间类 F(满足以下两个条件)
- F extends Runnable:继承 Runnable
- F { primary Callable c }:组合 Callable
=> 最终,结合上面的所有分析,我们得到了 FutureTask 的结构:实现 Future 接口 & 实现Runnable接口 & 组合Callable
FutureTask 到底是怎么样的,请看下一篇文章 FutureTask 源码分析…