FutureTask分析
基于jdk11 但是和1.8有一些小差别
先看一下简单使用
//静态内部类创建单例线程池 -利用了类机制
static class SinglePool{
public static ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 50, 2000, TimeUnit.SECONDS, new ArrayBlockingQueue(100));
}
/**
* 通过Calllable来创建线程,这个接口你补了前面两种执行完任务没有返回值的缺点,
* 这个方法执行完任务需要返回值
*/
static class Thread3 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum=0;
TimeUnit.MILLISECONDS.sleep(1000);
for (int i=0 ; i< 100 ; i++){
sum+=i;
}
return sum;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1.创建一个实现了Calllable的类
Thread3 thread3 = new Thread3();
//2.创建FutureTask 把步骤一的类(任务)丢个FutureTask去执行
//
FutureTask task = new FutureTask(thread3);
//3.把FutureTask的任务开辟一条线程去执行。
new Thread(task,"线程三").start();
if (!task.isDone()){
System.out.println("任务没完成"+task.isDone());
}
if (!task.isCancelled()){
System.out.println("任务是否取消"+task.isCancelled());
}
// if (task.cancel(true)) {
// System.out.println("取消任务");
// }
// if (task.isCancelled()){
// System.out.println("再去确认任务是否取消"+task.isCancelled());
// }
//线程一new Thread(()-> System.out.println(Thread.currentThread().getName()+"线程A"),"线程A").start();
//线程二
new Thread(()-> System.out.println(Thread.currentThread().getName()+"线程B"),"线程B").start();
System.out.println(task.get());
System.out.println(Thread.currentThread().getName()+"任务执行完毕");
}
}
控制台输出:
任务没完成false
任务是否取消false
线程A线程A
线程B线程B
4950
main任务执行完毕
//futureTask是继承了RunnableFuture接口,而这个接口继承了runnable和future接口
public class FutureTask implements RunnableFuture {
//记录当前状态,使用了volatile关键字,作用使线程之间修改状态是可见的
private volatile int state;
//任务初始化状态
private static final int NEW = 0;
//任务call方法已经结束设置任务结果但还没设置到outCome
private static final int COMPLETING = 1;
//正常执行任务成功
private static final int NORMAL = 2;
//异常状态,保存异常信息
private static final int EXCEPTIONAL = 3;
//正在取消任务,只有调用了cancel才会使用
private static final int CANCELLED = 4;
//正在尝试终端线程,但不一定成功
private static final int INTERRUPTING = 5;
//线程被中断后的状态
private static final int INTERRUPTED = 6;
//继承了callable接口的任务类
private Callable callable;
///执行完的结果
private Object outcome; // non-volatile, protected by state reads/writes
//负责运行callable任务的线程
private volatile Thread runner;
//用于指向单链表的头节点的节点
private volatile WaitNode waiters;
}
看一下内部类的数据结果
//内部wiatNode类,是一个单链表结果
static final class WaitNode {
//全部都使用了volatile标识,当状态改变在各个线程之间都是可见的
volatile Thread thread;
//后继节点(每一个节点存储的是调用该方法时的线程线程)
volatile WaitNode next;
//把当前线程设置为thread
WaitNode() { thread = Thread.currentThread(); }
}
接着看一下构造方法
FutureTask由于继承了Runnable,所以支持callable和runnable,如果传入的是runnable,它会调用一个适配器类把把runnable和callable 适配。
//callable的构造方法,正常赋值
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // 状态是初始化状态
}
/**
* runnable的构造方法
*/
public FutureTask(Runnable runnable, V result) {
//通过Executors内部的适配器类进行适配
this.callable = Executors.callable(runnable, result);
this.state = NEW; // 设置为初始化状态
}
接着看一下run方法这些执行任务的方法
run方法有几种情况,
public void run() {
//判断状态和cas尝试操作把当前线程设置,判断是否是第一次,如果不是直接返回
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return;
try {
//把c指向callable
Callable c = callable;
//如果c不为空,而且state是初始化状态
if (c != null && state == NEW) {
//用来装指向完的结果
V result;
//判断任务是否执行完成
boolean ran;
try {
//执行实际任务,并赋值结果给result
result = c.call();
//设置为true表示已经完成任务
ran = true;
} catch (Throwable ex) {
//异常则设置为false
result = null;
ran = false;
//调用设置异常状态
setException(ex);
}
//不管有没有异常都要把结果设置
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
//最后把执行任务的线程指控方便 GC回收
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
//判断状态是否处于正在中断/或者中断
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
//该方法是一直处于自旋一直到状态为中断状态才进行礼让
private void handlePossibleCancellationInterrupt(int s) {
if (s == INTERRUPTING)
while (state == INTERRUPTING)
Thread.yield(); // wait out pending interrupt
}
接着方向set(result)方法
protected void set(V v) {
//修改状态,把new,该往正在设置结果
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
//把结果赋值给outcome
outcome = v;
//状态修改为正常结束
STATE.setRelease(this, NORMAL); // final state
//最后调用下面的方法唤醒链表中的线程节点
finishCompletion();
}
}
该方法是内部类waitNode的方法
private void finishCompletion() {
// 遍历链表,如果头节点不为空的情况,如果为空则证明没有等待阻塞的线程
//否则遍历唤醒
for (WaitNode q; (q = waiters) != null;) {
//如果需要唤醒的头节点(waiters是唤醒节点的头节点)就是当前节点则把当前线程节点置空
if (WAITERS.weakCompareAndSet(this, q, null)) {
for (;;) {//死循环唤醒
//获取当前节点的线程
Thread t = q.thread;
//线程不为空
if (t != null) {
//将等待阻塞线程置空
q.thread = null;
//并通过lockSupport唤醒在等待阻塞的唤醒线程
LockSupport.unpark(t);(partk()方法是让当前阻塞等待)
}
//获取下一个等待节点
WaitNode next = q.next;
//如果为空则结束
if (next == null)
break;
否则把q的后继节点置空
q.next = null; // unlink to help gc
//把下一个节点置给q,继续循环上述步骤一直把所以等待的线程唤醒为止
q = next;
}
break;
}
}
// 这个方法在FutureTask是空实现的,是一个钩子方法
done();
//将callable置空
callable = null; // to reduce footprint
}
接着看获取结果的get方法是如何执行的
get方法有两种,
一种是一直阻塞式获取结果
判断当前状态,如果状态标识已经处于初始化或者正在设置结果状态则直接通过report获取结果,
换句话说,除了new和Completing状态其他状态一律任务是任务执行完成,是否正常完成则由report方法来进一步判断。如果是处于前面两种状态则 开始调用awaitDone方法阻塞其他线程等待任务执行结束
一种是限时阻塞时获取结果
执行逻辑与上述差不多
//这是一直阻塞式 的get方法,如果任务没执行完成会让其他线程一直处于阻塞状态,阻塞状态的的线程则会用waitnode节点存储并通过调用part方法挂起来(从用户态切到内核态)
public V get() throws InterruptedException, ExecutionException {
//判断当前状态,如果状态标识已经处于初始化或者正在设置结果状态则直接通过report获取结果,
//换句话说,除了new和Completing状态其他状态一律任务是任务执行完成,是否正常完成则由report方法来进一步判断。如果是处于前面两种状态则 开始调用awaitDone方法阻塞其他线程等待任务执行结束
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
/**
* 限制时间获取结果,这种我称为限时阻塞
*/
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
//判断是参数是否合法
if (unit == null)
throw new NullPointerException();
//判断和上面是一样的只是awaitDonw方法参数改变为有时间判断了
int s = state;
if (s <= COMPLETING &&
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
throw new TimeoutException();
return report(s);
}
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
//设置开始时间为0
long startTime = 0L;
//阻塞等待节点为q 当前为null
WaitNode q = null;
//是否出队为false
boolean queued = false;
for (;;) {
//获取当前状态并且进行判断
int s = state;
//如果大于1则任务线程的任务已经执行完成或者取消/前面有讲只有前面两种状态才任务任务没执行完成
if (s > COMPLETING) {
//节点不为null则把节点的线程置空,任务的完成了并返回当前线程状态
if (q != null)
//则把
q.thread = null;
return s;
}
//否则正在设置结果状态
else if (s == COMPLETING)
// We may have already promised (via isDone) that we are done
// so never return empty-handed or throw InterruptedException
//如果当前线程正在执行设置执行完成的结果,则当前线程进行礼让,让出cpu资源给任务线程进行任务,进入等待状态
Thread.yield();
else if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
//如果节点是空,则根据当前线程创建一个等待节点
else if (q == null) {//判断时间
if (timed && nanos <= 0L)
return s;
q = new WaitNode();
}
//如果没入队,则通过waiteters调用cas把当前线程入队而且是头节点
else if (!queued)
queued = WAITERS.weakCompareAndSet(this, q.next = waiters, q);
else if (timed) {//判断时间
final long parkNanos;
if (startTime == 0L) { // first time
startTime = System.nanoTime();
if (startTime == 0L)
startTime = 1L;
parkNanos = nanos;
} else {
//如果超时则异常该线程节点
long elapsed = System.nanoTime() - startTime;
if (elapsed >= nanos) {
removeWaiter(q);
return state;
}
parkNanos = nanos - elapsed;
}
// nanoTime may be slow; recheck before parking
//如果还没完成,则继续挂起
if (state < COMPLETING)
LockSupport.parkNanos(this, parkNanos);
}
else//否则阻塞线程,等待唤醒
LockSupport.park(this);
}
}
//report方法是获取结果返回的方法
private V report(int s) throws ExecutionException {
//把outcome结果赋值给x
Object x = outcome;
//判断是否是正常执行任务完成,如果是则正常返回
if (s == NORMAL)
return (V)x;
//如果是取消任务则抛取消异常
if (s >= CANCELLED)
throw new CancellationException();
//否则抛执行异常
throw new ExecutionException((Throwable)x);
}
取消任务的方法
//传入参数 是否取消
public boolean cancel(boolean mayInterruptIfRunning) {
//如果当前状态不等于new,并且修改状态为中断状态失败则返回false
if (!(state == NEW && STATE.compareAndSet
(this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
//否则进行中断
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
//获取运行任务线程,不等于kon则进行中断
Thread t = runner;
if (t != null)
t.interrupt();
//最后修改状态
} finally { // final state
STATE.setRelease(this, INTERRUPTED);
}
}
} finally {
//然后唤醒其他等待阻塞的线程节点
finishCompletion();
}
return true;
}
总结:FutureTask是一个异步任务类,整个大概运行过程是:创建一个FutureTask,并且传入任务类,如果任务是callable类型则直接创建,否则就是Runnable类型,则通过Executors的内部适配器类,对Callable和Runnable进行适配(这里使用了适配器模式),创建FutureTask完成后,则交给线程池或者线程,开启异步任务,这时候进入run方法,
run方法大致分三种情况
但是如果正在执行run方法,中途杀出个程咬金调用get方法的时候这个时候跳转到get方法看如何出来调用get方法的线程(这里就不分,是否限制时间获取结果,统一分析无限制,一直阻塞的get方法)
FutureTask+Callable+线程/或者线程池,内部是通过维护任务的状态、阻塞等待队列(链表)和CAS操作来实现的,而且底层使用了大量的Volatle+Cas操作