FutureTask的应用

让我们先写一个demo

class Task implements Callable<Integer> {@Overridepublic Integer call() throws Exception {Thread.sleep(2);return 1;}
}
public class test {private int x;public static void main(String[] args) throws Exception {ExecutorService executorService = Executors.newCachedThreadPool();Future<Integer> future = executorService.submit(new Task());System.out.println(future.get());}
}

我们知道,实际上线程创建只有两种方式Runnable和Callable,而使用Callable,在线程池中,需要使用submit()才有返回值,所以我们进入submit()


不难看出,submit()做了一件事,它内部调用了一个叫newTaskFor的方法,这个方法会调用FutureTask的构造方法,所以我们可以得出结论
在线程池中,无论是出于异步的目的,还是单纯想要有返回值,都避不开FutureTask

也许有人会觉得,我可以直接execute来跳过submit(),然而在ThreadPoolExecutor类中我们可以看到,只有一个方法public void execute(Runnable command) ;也不存在它的重载方法,也就是说不存在返回值。
所以唯一的方法就是使用FutureTask,这个类实现了RunnableFuture接口,RunnableFuture接口同时继承了Runnable和Future两个接口,可以满足execute(Runnable command)的调用

构造方法


增加任务,可以选择Callable或Runnable,需要注意的是,Runnable也可以有返回值,因为它调用了Executor当中的RunnableAdapter,可以将Runnable适配成Callable

FutureTask有着自己的结果存储变量outcome,任务的返回值就是这个值,往往通过get()来获得它(如第一个demo所示)

主要参数

WaitNode 保存自己当前线程和下一个WaitNode的指针,这是个用来装相同任务线程的无锁并发栈,也就是说,如果未来有线程拥有同样任务,那么加入的方式是头插法,无锁并发是通过CAS来保证一致性的。


NEW 正在运行或将要运行
COMPLETING 完结前的操作
NORMAL 正常完结
EXCEPTIONAL 异常完结
CANCELLED 取消
INTERRUTING 正在进行中断操作
INTERRUTED 任务已经被标记中断

状态转变仅存在三个方法,set(),setException(),cancel()
在COMPLETING状态和INTERRUTING状态是懒写入的,因为值唯一并且在后来无法更改

基本操作

放入结果


作用:在成功完成计算的基础上run()内部会调用set()来设置值,将任务结果输入到指定outcome,除非已有相同的任务或已被取消。

状态变化:NEW->COMPLETING->NORMAL

逐个节点thread置空,唤醒等待线程,然后执行FutureTask的done(),然后将任务置空

这个方法在正常结束(NORMAL)和被取消(CANCELLED)时会被调用
但并没有具体实现,子类继承该方法用于在任务完成时回调或者记录总数(这是官方推荐的)。可以在方法的实现中查询状态来确定任务是否被取消了。

get

该方法如果要在NEW和COMPLETING调用需要满足

    private int awaitDone(boolean timed, long nanos)throws InterruptedException {// The code below is very delicate, to achieve these goals:// - call nanoTime exactly once for each call to park// - if nanos <= 0L, return promptly without allocation or nanoTime// - if nanos == Long.MIN_VALUE, don't underflow// - if nanos == Long.MAX_VALUE, and nanoTime is non-monotonic//   and we suffer a spurious wakeup, we will do no worse than//   to park-spin for a whilelong startTime = 0L;    // Special value 0L means not yet parkedWaitNode q = null;boolean queued = false;for (;;) {int s = state;if (s > COMPLETING) {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 InterruptedExceptionThread.yield();else if (Thread.interrupted()) {removeWaiter(q);throw new InterruptedException();}else if (q == null) {if (timed && nanos <= 0L)return s;q = new WaitNode();}else if (!queued)queued = WAITERS.weakCompareAndSet(this, q.next = waiters, q);else if (timed) {final long parkNanos;if (startTime == 0L) { // first timestartTime = 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 parkingif (state < COMPLETING)LockSupport.parkNanos(this, parkNanos);}elseLockSupport.park(this);}}

如果是在COMPLETING状态,会直接释放CPU给其它线程
否则
第一次会进入else if (q == null) ,并新建waitNode节点
第二次会进入else if (!queued),通过CAS加入队列,weakCompareAndSet和compareAndSet的区别在于,前者是volatile非增强语义,后者是volatile增强语义
完成了初始化,后面将会一直进入 else if (timed),如果超时,就会删除节点并返回状态,而期间会有parkNanos记录剩余到达删除时间的纳秒数,这是为了后期挂起的线程还有执行时间而不是挂起
也就是说,<=completing一定会让出CPU,completing会处于就绪态而在此之前的状态会处于阻塞态
而走到这里如果依然处于NEW状态,就会进入WAITING,等待唤醒

如果没有许可,并且设置时间有效,那么就会挂起线程
唤醒条件:
1,被unpark
2,被中断,并且唤醒后不会报异常
3,时间到

最后的report()

用于返回结果,也可能抛出告知取消,为什么中断也会抛出取消呢,首先我们要明白一个前置条件,那就是只有get()和定时get()两个重载方法会调用report(),所以如果是一个新任务,那么它必定走到get()中的park()

而park()之后被唤醒情况有两种,第一种,set()内部调用finishCompletion()后把所有等待这个任务的线程释放,第二种,任务被取消了,运行线程会被中断,等待线程也会被内部调用的finishCompletion()给唤醒
第一种情况就是NORMAL,第二种情况就是CANCELLED
以下是第二种情况的具体流程

压轴-执行方法!

执行方法有两个-run()和runAndReset(),这两者的区别是
1 runAndReset()会反复执行,除非取消或遇到了异常,适用于程序内部循环调用。
2 runAndReset()不会保存结果,也就是outcome不会被赋值

让我们分析一下这个方法
首先CAS保证了这个任务只被启动了一次,而handlePossibleCancellationInterrupt()用于协助取消任务,这一步是为了防止漏掉中断,如果有其他线程正在执行cancel()(cancel源码在get()分析最后面),他可能刚标记为INTERRUPTING还没来得及执行interrupt()然后run()就跑完了。

让我们看看另一个-run()

如果执行过且无异常就会将结果记入,记入函数set()在之前已经分析过了

硬核FutureTask解析相关推荐

  1. TCP协议疑难杂症全景解析|硬核

    大家好,我是Alex,今天分享一篇好文章,来自好友dog250,本文深入浅出地分析了TCP协议为什么要这样设计的背后原因,解答了几乎所有TCP疑难杂症问题,可以帮助大家查缺补漏,加深对TCP协议的理解 ...

  2. SSD之硬的不能再硬的硬核解析

    本文是对经典论文 SSD: Single Shot MultiBox Detector 的解析,耗时3周完成,万字长文,可能是你能看到的最硬核的SSD教程了,如果想一遍搞懂SSD,那就耐心读下去吧~ ...

  3. 全网最硬核 Java 新内存模型解析与实验 - 2. 原子访问与字分裂

    个人创作公约:本人声明创作的所有文章皆为自己原创,如果有参考任何文章的地方,会标注出来,如果有疏漏,欢迎大家批判.如果大家发现网上有抄袭本文章的,欢迎举报,并且积极向这个 github 仓库 提交 i ...

  4. 全网最硬核 JVM TLAB 分析 5. TLAB 源代码全解析

    今天,又是干货满满的一天.这是全网最硬核 JVM 系列的开篇,首先从 TLAB 开始.由于文章很长,每个人阅读习惯不同,所以特此拆成单篇版和多篇版 全网最硬核 JVM TLAB 分析(单篇版不包含额外 ...

  5. 全网最硬核 Java 新内存模型解析与实验 - 1. 什么是 Java 内存模型

    个人创作公约:本人声明创作的所有文章皆为自己原创,如果有参考任何文章的地方,会标注出来,如果有疏漏,欢迎大家批判.如果大家发现网上有抄袭本文章的,欢迎举报,并且积极向这个 github 仓库 提交 i ...

  6. 全网最硬核 Java 新内存模型解析与实验 - 4. Java 新内存访问方式与实验

    个人创作公约:本人声明创作的所有文章皆为自己原创,如果有参考任何文章的地方,会标注出来,如果有疏漏,欢迎大家批判.如果大家发现网上有抄袭本文章的,欢迎举报,并且积极向这个 github 仓库 提交 i ...

  7. 全网最硬核 Java 新内存模型解析与实验 - 3. 硬核理解内存屏障(CPU+编译器)

    个人创作公约:本人声明创作的所有文章皆为自己原创,如果有参考任何文章的地方,会标注出来,如果有疏漏,欢迎大家批判.如果大家发现网上有抄袭本文章的,欢迎举报,并且积极向这个 github 仓库 提交 i ...

  8. 全网最硬核 JVM TLAB 分析(额外加菜) 7. TLAB 相关 JVM 日志解析

    今天,又是干货满满的一天.这是全网最硬核 JVM 系列的开篇,首先从 TLAB 开始.由于文章很长,每个人阅读习惯不同,所以特此拆成单篇版和多篇版 全网最硬核 JVM TLAB 分析(单篇版不包含额外 ...

  9. 阿里达摩院青橙奖颁出:10名85后每人100万!其中有硬核粉红少女,还有人被LeCun点名支持...

    杨净 萧箫 发自 凹非寺 量子位 报道 | 公众号 QbitAI 刚刚,这10个年轻学者,获得了阿里达摩院2020年青橙奖,每人奖金100万元. 达摩院院长张建锋公布了详细名单: 他们是: 梁文华,广 ...

最新文章

  1. OpenAI Gym介绍
  2. php绘制奥运五环(GD)
  3. C语言求35 45的最大公约数,C语言怎么求最大公约数和最小公倍数
  4. linq 分组求和的一般方法
  5. 【转】Wireshark网络抓包(一)——数据包、着色规则和提示
  6. IE9为啥没有加载进度进度提示
  7. android imageview图片崩溃,android - setImageResource导致应用程序崩溃 - 堆栈内存溢出...
  8. HDU3787 A+B【进制】
  9. 关于怕什么来什么的说法,是否成立
  10. 【计算机网络微课堂】3.3 差错检测
  11. 普加甘特图数据结构解析
  12. python爬虫国家企业信息网_国家企业信用信息公示系统爬虫
  13. js 判断3D空间中 三点共线
  14. 晚上思考人生千条路,白天走原路
  15. 完全免费一级域名强注册地址 免费二级域名注册地址
  16. 【技术干货】详解BGP4+的负载分担
  17. 计算机哪个按键可以和弦,钢琴键盘和弦图解大全!作曲必看!老师和家长快收藏起来...
  18. Oracle数据库块之旅
  19. PAT A1129 重载小于号快速解答
  20. 【C++之GDB调试】GDB调试从入门到精通

热门文章

  1. 集算器读写 json
  2. platform.js
  3. Java初学之人机猜拳游戏
  4. 定义一个点类(Point)、圆类(Circle)和圆柱体类(Cylinder)的层次结构。圆包括圆心和半径两个数据成员,圆心具有点类的所有特征。圆柱体类由半径和高构成。要求各类提供支持初始化的构造函数
  5. 视觉培训2 机器学习基础知识学习
  6. 钰泰半导体ETA4034爆款OVP+OCP+NTC+OTP+FAULT五合一方案, 兼容BQ24314
  7. python 导入sklearn时报错: no model named ‘murmurhash‘
  8. GPS天线类型和特点
  9. PAT(甲级) 1003. Emergency
  10. Unity计时器功能