目录

前言

一、状态

二、运行流程分析

1.run

2.get

3.cancel

4.runAndReset

三、ListenableFutureTask

总结


前言

实现了Runnable接口的类能够新建线程运行,Future接口规范了线程的生命周期,Callable接口能够获得方法的返回值。FutureTask实现了Runnable和Future接口,同时有Callable属性,能够实现三者的功能。


一、状态

FutureTask有NEW,COMPLETING,NORMAL,EXCEPTIONAL,CANCELLED,INTERRUPTING和INTERRUPTED七种状态,创建时状态为NEW,结果未赋值时更新为COMPLETING,结果赋值后更新为NORMAL,发生异常后会将状态置为EXCEPTIONAL,调用cancel方法后更新为CANCELLED或者INTERRUPTING状态。

* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
    public class FutureTask<V> implements RunnableFuture<V> {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; //任务已被中断
    /** The underlying callable; nulled out after running */private Callable<V> callable;/** The result to return or exception to throw from get() */private Object outcome; // non-volatile, protected by state reads/writes/** The thread running the callable; CASed during run() */private volatile Thread runner;/** Treiber stack of waiting threads */private volatile WaitNode waiters;

二、运行流程分析

1.run

run方法负责任务的执行,在该方法中,执行Callable对象的call方法,如果在运行过程中发生异常,会将异常写入outcome中,正常执行完成将运行结果写入outcome中。

    //运行完成之后将返回值设置为outcome,可以通过get方法获取,call方法有返回值。FutureTask实现了runniable接口,重写run方法如下,public void run() {if (state != NEW || //如果状态不属于NEW,那么通过CAS将runner变量由null设为当前线程,!UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread()))return;try {  //获取构造时传入的Callable任务对象Callable<V> c = callable;if (c != null && state == NEW) {  //如果状态为NEWV result;boolean ran;try {result = c.call(); //调用Callable的call方法执行任务ran = true;} catch (Throwable ex) {result = null;ran = false;setException(ex); //设置异常对象,由调用get方法的线程处理这个异常}if (ran)  //如果任务正常结束则设置返回值和state变量set(result);}} finally {// runner must be non-null until state is settled to// prevent concurrent calls to run()runner = null;// state must be re-read after nulling runner to prevent// leaked interruptsint s = state;if (s >= INTERRUPTING)  //如果处于任务正在中断状态,则等待直到任务处于已中断状态位置handlePossibleCancellationInterrupt(s);}}

当运行call方法出现异常之后,会调用setException方法,在该方法中,首先尝试将状态更新为COMPLETING,再将异常赋值给outcome,然后将状态更新为EXCEPTIONAL。

    protected void setException(Throwable t) {if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { //将状态更新为将要完成状态outcome = t;UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state //将状态更新为发生异常状态finishCompletion();}}

方法运行结束之后会调用set方法,在该方法中首先将当前状态更新为COMPLETING,然后将运行结果赋值给outcome,之后将状态更新为NORMAL。

    protected void set(V v) {if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { //将状态更新为将要完成状态outcome = v;UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state //将状态更新为正常完成状态finishCompletion();}}
     *///在中断者中断线程之前可能会延迟,所以我们只需要让出CPU时间片自旋等待private void handlePossibleCancellationInterrupt(int s) {// It is possible for our interrupter to stall before getting a// chance to interrupt us.  Let's spin-wait patiently.if (s == INTERRUPTING)while (state == INTERRUPTING)Thread.yield(); // wait out pending interrupt}

在对结果进行赋值之后,会调用finishCompletion方法,在该方法中,首先对等待获取结果的线程进行唤醒,然后执行done方法。

     *///唤醒因为等待结果而阻塞的线程private void finishCompletion() {// assert state > COMPLETING;for (WaitNode q; (q = waiters) != null;) {if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) { //移除等待线程for (;;) {//自旋遍历等待线程Thread t = q.thread;if (t != null) {q.thread = null;LockSupport.unpark(t);//唤醒等待线程}WaitNode next = q.next;if (next == null)break;q.next = null; // unlink to help gcq = next;}break;}}//结束之后调用,可以进行重写done();callable = null;        // to reduce footprint}

2.get

在get方法中,如果当前状态小于等于COMPLETING状态,即当前Task还在运行中,使用awaitdown方法,当task执行完成之后调用report方法。

    public V get() throws InterruptedException, ExecutionException {int s = state;if (s <= COMPLETING)s = awaitDone(false, 0L);return report(s);}

在awaitDone方法中,还是判断当前任务有没有执行完成,执行完成后将状态返回,多次判断后仍然没有执行完成会将当前线程加入等待队列中,并阻塞。

    private int awaitDone(boolean timed, long nanos)throws InterruptedException {final long deadline = timed ? System.nanoTime() + nanos : 0L;WaitNode q = null;boolean queued = false;for (;;) {//如果线程中断if (Thread.interrupted()) { //获取并清除中断状态removeWaiter(q);//移除等待WaitNodethrow new InterruptedException();}int s = state;//如果任务执行结束或被取消(中断),方法结束,返回结果if (s > COMPLETING) {if (q != null)q.thread = null;return s;}else if (s == COMPLETING) // cannot time out yet //如果任务即将完成,让当前线程让步,让出cpuThread.yield();else if (q == null)q = new WaitNode();else if (!queued) //如果没有入队,将这个WaitNode加入到FutureTask的等待队列尾部queued = UNSAFE.compareAndSwapObject(this, waitersOffset,q.next = waiters, q);else if (timed) { //如果设置了超时时间nanos = deadline - System.nanoTime();if (nanos <= 0L) {removeWaiter(q);return state;}LockSupport.parkNanos(this, nanos);}else //阻塞当前线程LockSupport.park(this);}}

在report方法中,如果任务正常执行完成,就返回结果,如果任务被取消,就抛出异常,如果任务执行过程中出现异常,即处于EXCPTIONAL状态,就抛出所发生的异常。

    private V report(int s) throws ExecutionException {Object x = outcome;if (s == NORMAL)  //如果任务正常执行完成直接返回结果return (V)x;if (s >= CANCELLED) //如果任务被取消或被中断抛出CancellationException(运行时异常的子类)throw new CancellationException();throw new ExecutionException((Throwable)x);}

3.cancel

cancel方法负责将任务取消,更改线程状态,最后还会唤醒等待结果的线程。

    public boolean cancel(boolean mayInterruptIfRunning) {if (!(state == NEW && //如果任务状态为NEW并且成功通过CAS将state状态由NEW改为INTERRUPTING或CANCELLED(视参数而定)UNSAFE.compareAndSwapInt(this, stateOffset, NEW,mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))return false;try {    // in case call to interrupt throws exceptionif (mayInterruptIfRunning) {try {Thread t = runner;if (t != null)t.interrupt(); //调用interrupt中断} finally { // final state //将state状态设为INTERRUPTED(已中断)UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);}}} finally {finishCompletion();  //激活所有在等待队列中的线程}return true;}

4.runAndReset

runAndReset方法会在运行完任务之后,将任务状态重置,但不会对结果进行赋值,在定时任务线程池中有所应用。

     *///任务执行完之后会重置stat的状态为NEWprotected boolean runAndReset() { //和run方法类似,通过CAS操作将成员变量runner设置为当前线程if (state != NEW ||!UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread()))return false;boolean ran = false;int s = state;try {Callable<V> c = callable;if (c != null && s == NEW) {try {//不会调用set方法设置返回值(outcome成员变量)c.call(); // don't set resultran = true;} catch (Throwable ex) {setException(ex);}}} finally {// runner must be non-null until state is settled to// prevent concurrent calls to run()runner = null;// state must be re-read after nulling runner to prevent// leaked interruptss = state;if (s >= INTERRUPTING)handlePossibleCancellationInterrupt(s);}   //这个方法不会修改state变量的值return ran && s == NEW;}

三、ListenableFutureTask

FutureTask想要获取任务运行的结果,需要等待任务执行完成,如果调用时间太早,会导致调用线程阻塞,影响效率。在有些情况下,我们需要在任务执行完成之后执行相应的操作,同时结果不会影响主业务逻辑,我们希望在任务执行完成之后自动根据结果进行操作,而不是等主线程调用。spring对FutureTask进行了继承,重写了done方法,如果程序正常执行完成,会调用success方法,异常退出,会执行failure方法。

 public class ListenableFutureTask<T> extends FutureTask<T> implements ListenableFuture<T> {protected void done() {Throwable cause;try {T result = get();this.callbacks.success(result);return;}catch (InterruptedException ex) {Thread.currentThread().interrupt();return;}catch (ExecutionException ex) {cause = ex.getCause();if (cause == null) {cause = ex;}}catch (Throwable ex) {cause = ex;}this.callbacks.failure(cause);}
}

总结

本文对FutureTask的源码进行了分析,主要分析run,get,cancel和runAndSet方法,同时,对FutureTask的子类ListenableFutureTask进行了分析,其实现了异步回调功能。

FutureTask源码解析相关推荐

  1. Java并发编程之FutureTask源码解析

    上次总结一下AQS的一些相关知识,这次总结了一下FutureTask的东西,相对于AQS来说简单好多呀 之前提到过一个LockSupport的工具类,也了解一下这个工具类的用法,这里也巩固一下吧 /* ...

  2. FutureTask源码解析(2)——深入理解FutureTask 1

    Future和Task 在深入分析源码之前,我们再来拎一下FutureTask到底是干嘛的.人如其名,FutureTask包含了Future和Task两部分. FutureTask实现了Runnabl ...

  3. FutureTask源码解析二

    本篇主要介绍FutureTask源码 我们知道FutureTask实现了RunnableFuture接口,即Runnable接口和Future接口,Runable可以对应FutureTask的task ...

  4. java futuretask 源码解析_Java异步编程——深入源码分析FutureTask

    Java的异步编程是一项非常常用的多线程技术. 之前通过源码详细分析了ThreadPoolExecutor<你真的懂ThreadPoolExecutor线程池技术吗?看了源码你会有全新的认识&g ...

  5. Java多线程——FutureTask源码解析

    一个很常见的多线程案例是,我们安排主线程作为分配任务和汇总的一方,然后将计算工作切分为多个子任务,安排多个线程去计算,最后所有的计算结果由主线程进行汇总.比如,归并排序,字符频率的统计等等. 我们知道 ...

  6. Android之AsyncTask源码解析

    转载请标明出处:[顾林海的博客] 个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持! ##前言 AsyncTask是一种轻量级的异步任务类,内部封装了Thread和Ha ...

  7. Android之图片加载框架Picasso源码解析

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/76645535 本文出自:[顾林海的博客] 个人开发的微信小程序,目前功 ...

  8. 面试官系统精讲Java源码及大厂真题 - 37 ThreadPoolExecutor 源码解析

    37 ThreadPoolExecutor 源码解析 当你做成功一件事,千万不要等待着享受荣誉,应该再做那些需要的事. -- 巴斯德 引导语 线程池我们在工作中经常会用到.在请求量大时,使用线程池,可 ...

  9. 面试官系统精讲Java源码及大厂真题 - 28 Future、ExecutorService 源码解析

    28 Future.ExecutorService 源码解析 今天应做的事没有做,明天再早也是耽误了. 引导语 本章和大家一起看下有返回值的线程如何创建,两种线程 API 之间如何关联,介绍一下和线程 ...

最新文章

  1. C# 准备开始学习 并行程序开发
  2. ElasticSearch之Java Api 测试
  3. 统计寄存器AX中1 的个数
  4. UPX命令行压缩、反汇编动画进入、OllyDbg搜索命令功能
  5. R语言实战应用精讲50篇(二十五)-时空数据统计模型:确定性预测模型
  6. 海量存储系列下--转载,值得一读
  7. Solr配置IK分词器
  8. 转载:介绍几本专业的书籍,一起学习
  9. 如何选择深度学习优化器
  10. SAP C4C里前台Opportunity搜索的响应明细
  11. Logstash 命令行参数
  12. bios是固定在微型计算机上的一块RoM,计算机硬件笔试试题
  13. centos树莓派安装mysql_树莓派3B+安装CentOS7
  14. Java构造方法的继承调用
  15. 多并发编程基础 之进程 Process
  16. VB程序设计练习题(一)
  17. 计算机学报Latex模板运行出错解决
  18. 人工智能就是计算机科学的英文,AI(人工智能)的英文全称?AI指什么,包含什么?
  19. 抖音多闪背后的AI和社交
  20. Graphene(石墨烯)区块传播技术能够实现10倍的更高效率

热门文章

  1. 公有 IP 和私有 IP 的区别
  2. CTF-日常密码泄露分析溯源
  3. html如何提取图片颜色代码,PS怎么提取颜色做色卡?
  4. 从零开始实现放置游戏(三):后台管理系统搭建
  5. 使用vs建立C/C++项目
  6. 某班有5名同学,建立一个学生的简单信息表,包括学号、姓名、3门课程的成绩,编写程序,计算每名学生的平均成绩及名次。(30分) 题目内容: 某班有5名同学,建立一个学生的简单信息表,包括学号、姓名、3
  7. Netlify前端自动化部署服务
  8. Phishing钓鱼邮件平台搭建
  9. 临近毕业,2020春招困惑你的十大问题,你中招了吗?
  10. vscode使用vetur解决代码换行、格式化、常量问题