**FutureTask作用:**支持创建有返回值的线程。 FutureTask可以很容易获得异步任务的执行结果,无论异步任务是通过线程池 ThreadPoolExecutor 执行的,还是通过手工创建子线程来执行的
总体架构
FutureTask从关系图中发现实现了RunnableFuture,而FunnableFuture继承了Future接口和Runnable接口。其关系类图及主要方法如下:
类注解信息:
- 可取消的异步计算。这个类提供了一个基于Future实现的,带有开始和取消方法一个计算,查询看是否计算完成,和检索计算结果。结果只能是计算完成后检索,如果计算尚未完成,方法将被阻塞。一次计算已完成,无法重新开始计算或取消(除非计算被调用
#runAndReset
- 一个FutureTask可以用来包装一个Callable或者Runnable对象,因为FutureTask实现了Runnable接口,同时一个FutureTask可以提交给 Executor执行(submit)。
- 除了作为一个独立的类,这个类还提供在创建时可能有用的功能自定义任务类。
FutureTask是Future的实现,Future定义了一些重要的接口,其源码如下:
public interface Future<V> {
/**
* 取消任务的方法,,但一旦任务计算完成,就无法被取消了
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* 判断任务是否已取消的方法
*/
boolean isCancelled();
/**
* 判断任务是否已结束的方法
*/
boolean isDone();
/**
* 等待结果返回
* 如果任务被取消了,抛 CancellationException 异常
* 如果等待过程中被打断了,抛 InterruptedException 异常
*/
V get() throws InterruptedException, ExecutionException;
/**
* 等待,但是带有超时时间的,如果超时时间外仍然没有响应,抛 TimeoutException 异常
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Callable
Callable约定了任务中具体执行的业务逻辑,其与Runnable功能大体一致(都是函数式接口),主要区别为Callable是有返回值,而Runable没有返回值,Callable和FutureTask结合使用效果更佳哦。
其源码如下
@FunctionalInterface // 通过这个注解表示为函数式接口
public interface Callable<V> {
V call() throws Exception;
}
// 根据Callable接口的泛型返回一个泛型的结果
@FunctionalInterface
public interface Runnable {
// 无返回值
public abstract void run();
}
使用实例
通过编写一个分工合作的实例,来模拟实际开发中FutureTask的作用(模拟业务场景)
public class FutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("通过普通的新建线程方式启动");
FutureTask<String> ft2 = new FutureTask<>(new Task2());
FutureTask<String> ft1 = new FutureTask<>(new Task1(ft2));
Thread thread1 = new Thread(ft1);
Thread thread2 = new Thread(ft2);
thread1.start();
thread2.start();
System.out.println("----------------------------");
// 首先我们创建了一个线程池
System.out.println("通过线程池的方式提交futuretask");
ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 3,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>());
executor.submit(ft1);
executor.submit(ft2);
String s = ft1.get();
String s2 = ft2.get();
System.out.println(s);
System.out.println(s2);
}
}
class Task1 implements Callable<String> {
private FutureTask<String> task2;
public Task1(FutureTask<String> futureTask) {
this.task2 = futureTask;
}
@Override
public String call() throws Exception {
System.out.println("Task2 ————>在等待任务一的时候准备一下其他必要的事情");
System.out.println("Task2 ————>什么都好了,稍微等待任务2");
if ("".equals(task2.get()) || task2.get() != null) {
System.out.println("Task2 ————>拿到任务2提供的材料了");
task2.get();
System.out.println("Task2 ————>开始组装了");
System.out.println("Task2 ————>完成");
return "Task2 ————>success";
}
return null;
}
}
class Task2 implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("Task1 ————>正在准备阶段不要慌");
System.out.println("Task1 ————>已经ok");
return "Task1 ————>你要的材料,给你!";
}
}
FutureTask重要成员变量
private volatile int state;
/** 任务的一些状态 */
private static final int NEW = 0; // 任务状态
private static final int COMPLETING = 1; //任务执行中
private static final int NORMAL = 2; //任务执行结束
private static final int EXCEPTIONAL = 3; //任务异常
private static final int CANCELLED = 4; //任务取消成功
private static final int INTERRUPTING = 5; //任务正在被打断中
private static final int INTERRUPTED = 6; //任务被打断成功
/** 通过组合模式 组合了 Callable (主要为兼容Callable和Runnable) */
private Callable<V> callable;
/** 异步线程返回的结果 */
private Object outcome; // non-volatile, protected by state reads/writes
/** 当前任务所运行的线程 */
private volatile Thread runner;
/** 记录调用 get 方法时被等待的线程 */
private volatile WaitNode waiters;
从FutureTask的一些成员变量和它的关系类图,可知FutureTask实现了RunnableFuture 接口,也就是说间接实现了 Runnnable 接口(RunnableFuture 实现了 Runnnable 接口),就是说 FutureTask 本身就是个 Runnnable,同时 FutureTask 也实现了 Future,也就是说 FutureTask 具备对任务进行管理的功能(Future 具备对任务进行管理的功能)。同时通过组合的方式内部成员变量维护着Callable属性,就可以供了两者(Callable和Runnable)一系列的转化方法,这样 FutureTask 就统一了 Callable 和 Runnable
FutureTask初始化方式:
FutureTask有且仅有两种初始化方式:
/**
* 传入Callable的方式初始化
*/
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
// 任务处于初始化中()
this.state = NEW; // ensure visibility of callable
}
/**
* 使用Runnable的方式作为初始化,V result作为返回类型,
* 因为Runnable没有返回值的,所以通过V 指定FutureTask返回类型
* 是通过Executors进行转换使其为Callable类型
*/
public FutureTask(Runnable runnable, V result) {
/**
* Executors.callable 方法把 runnable 适配成 RunnableAdapter,
* RunnableAdapter 实现了 callable,所以也就是把 runnable
* 直接适配成了 callable。
* 适配的源码如下Executors.callable
*/
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
// 主要是Calledable和Runnable两者直接不能做相互转换,
// 所以需要借助RunnableAdapter进行,两者作为转换进行适配
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
FutureTask一些重要方法
1、get方法.
FutureTask实现了Future接口的两个get方法,其中无参数的get()如下:
/**
* 等待结果返回如果任务被取消了则会抛出CancellationException 异常,
* 而在执行的任务的时候被中断会抛出InterruptedException
*/
public V get() throws InterruptedException, ExecutionException {
// 查看任务状态
int s = state;
// COMPLETING 表示任务执行中,如果任务处于属于任务执行中或者新建的时候。
// 则使用awaitDone方法进行等待,其中无参的get方法是不做等待处理。
if (s <= COMPLETING)
// 无限阻塞(false) 有点傻逼慎用,
s = awaitDone(false, 0L);
return report(s);
}
// 可以被打断,打断则抛出InterruptedException
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
// 计算出任务的等待时长,如果一直等待的话,终止时间为 0
final long deadline = timed ? System.nanoTime() + nanos : 0L;
// 声明一个为空的等待节点
WaitNode q = null;
// 默认不排队
boolean queued = false;
// 进入无线死循环
for (;;) {
// 再次期间,如果执行任务的线程被终止,则将其移除等待节点
// 并抛出终止异常
if (Thread.interrupted()) {
// 把当前线程移除等待节点
removeWaiter(q);
throw new InterruptedException();
}
// 查看当前任务的状态
int s = state;
// 执行完成之后,返回
if (s > COMPLETING) {
// 如果在等待节点不为空,则首先将thread置为空
if (q != null)
q.thread = null;
return s;
}
// 如果任务正在执行,则使其放弃使用当前的cpu的使用权(yield()),重新竞争
// 主要是为了防止cpu在等待获取任务结果时cpu飙升
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
// 如果等待节点为空,则新建一个用于存放等待的节点*(节点属性为当前线程)
else if (q == null)
q = new WaitNode();
// 默认不排队(false)
boolean queued = false;
// 默认第一次都会执行这里,执行成功之后,queued 就为 true,就不会再执行了
// 把当前 waitNode 当做 waiters 链表的第一个
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
// 如果设置了超时时间,并过了超时时间的话,从 waiters 链表中删除当前 wait
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
// 没有过超时时间,线程进入 TIMED_WAITING 状态
LockSupport.parkNanos(this, nanos);
}
else
// 没有设置超时时间,进入 WAITING 状态,无线等待,不可取。
LockSupport.park(this);
}
}
// 等待任务的节点
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
private V report(int s) throws ExecutionException {
// 线程要返回的值
Object x = outcome;
// 任务执行结束
if (s == NORMAL)
return (V)x;
// 如果被取消则抛出异常
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
带参数的get方法
带参数的get方法( get(long timeout, TimeUnit unit))源码如下:
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
// 如果没有完成则,调用awaitDone()方法进行等待,等待时间过长则抛出超时异常。
if (s <= COMPLETING &&
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
throw new TimeoutException();
return report(s);
}
get 方法虽然名字叫做 get,但却做了很多 wait 的事情,当发现任务还在进行中,没有完成时,就会阻塞当前进程,等待任务完成后再返回结果值。阻塞底层使用的是 LockSupport.park 方法,使当前线程进入 WAITING 或 TIMED_WAITING 状态。
运行方法run
1、run 方法是没有返回值的,通过给 outcome 属性赋值(set(result)),get 时就能从 outcome 属性中拿到返回值;
2、FutureTask 两种构造器,最终都转化成了 Callable,所以在 run 方法执行的时候,只需要执行 Callable 的 call 方法即可,在执行 c.call() 代码时,如果入参是 Runnable 的话, 调用路径为 c.call() -> RunnableAdapter.call() -> Runnable.run(),如果入参是 Callable 的话,直接调用。
/**
* run 方法可以直接被调用,也可以开启新的线程进行调用(Excetors.submit或者Thread.start)
*/
public void run() {
// 状态不是任务创建,或者当前任务已经有线程在执行了,直接返回
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable; // 为内部维护的Callable对象
// Callable 不为空,并且已经初始化完成
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 赋值给返回值
result = c.call();
ran = true;
} catch (Throwable ex) {
// 异常则将结果置为null
result = null;
ran = false;
setException(ex);
}
// 给 outcome 赋值
if (ran)
set(result);
}
} finally {
// 运行程序必须是非空的,直到状态被确定为防止并发调用运行()
runner = null;
// 在任务状态经过转轮后必须重新读取状态以防止中断泄露
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
FutureTask的取消方法
// 取消任务,如果任务正在执行,尝试打断
public boolean cancel(boolean mayInterruptIfRunning) {
//任务状态不是创建 并且不能把 new 状态置为取消,直接返回 false
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // 进行取消操作,打断可能会抛出异常,选择 try finally 的结构
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // 状态设置成已打断
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
// 清除线程
finishCompletion();
}
return true;
}
FutureTask的是否取消方法
public boolean isCancelled() {
// 直接放回状态
return state >= CANCELLED;
}
以上就是对FutureTask源码分析。