写点什么

FutureTask 的使用方法及实现原理,Java 后端路线图

  • 2022 年 4 月 22 日
  • 本文字数:1585 字

    阅读完需:约 5 分钟

this.state = NEW; // ensure visibility of callable


}


在 FutureTask 的构造函数中,对声明为 volatile 的 state 变量进行赋值操作后,callable 变量也会变得对于其他线程可见


2. 用一个 Treiber 栈保存等待线程


Treiber 栈是一个无锁数据结构,FutureTask 中的 waiters 变量指向这个栈的栈顶。名字有点吓人,其实很简单,就是一个无锁的线程安全的栈。入栈操作只通过一步 CAS 操作实现,即修改栈顶指针 waiters;出栈和在栈的中间执行删除操作通过特定的循环操作实现。


3. 取消操作 cancel()的实现


cancel 操作首先判断 state 是否等于 NEW,即判断计算线程(runner)是否已经开始。如果没有开始执行,则立即用 CAS 操作将状态改为 CANCELED 或 INTERRUPTING,防止 runner 开始执行计算工作。判断了 state 之后,紧接着用 compareAndSwap 对 state 进行修改而不是覆盖式的赋值,这是考虑到在判断 state 和修改 state 之间,state 可能被其他线程修改。顺便讲一下,state 的判断后面紧跟 CAS 操作,可以让两个原子操作合并成一个原子操作,


state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED) Java 开源项目【ali1024.coding.net/public/P7/Java/git】


如果状态已经不是 NEW,说明 runner 已经开始,此时根据入参 mayInterruptIfRunning 来决定是否给 runner 发中断。接下来,把 Treiber 栈中的等待线程全部唤醒并且移出。这里的唤醒动作考虑到可能有多个线程并发调用 cancel(),所以设计了一个循环 CAS 操作,对 Treiber 栈操作竞争,保证只有一个线程能够进入内层循环,代码如下:


private void finishCompletion() {


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 gc


q = next;


}


break;


}


}


done();


callable = null;


}


基于循环 CAS 操作对一个数据结构进行修改,是实现无锁数据结构的常用套路。


4. 等待线程中的等待操作 get()的实现


等待线程在启动 FutureTask 执行,在必须访问其计算结果的时候调用 get()。如果此时计算结果已经出来,则 get()不会阻塞,直接把 outcome 变量中保存的记过返回;如果还未计算完成,则会执行一个等待循环。这个等待循环与 AQS 加锁操作中的循环类似,但有些不同,比如它管理一个等待线程。这个循环不断的检查中断状态和 state:如果遇到中断则退出;如果 state 表示计算结束则退出;如果 state=COMPLETING,则自旋;如果已经超时,则退出;其他情况阻塞。FutureTask.get()的阻塞操作就是由这个循环实现的,具体见 get()和 awaitDone()方法的代码。


5. 计算线程所调用的 run()方法的实现


计算线程,例如线程池中的 worker,通过调用 FutureTask.run()开始执行计算工作。请参照 FutureTask.run 的代码,如下(建议对照代码阅读)。


public void run() {


if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset,


null, Thread.currentThread()))


return;


try {


Callable<V> c = callable 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源 ;


if (c != null && state == NEW) {


V result;


boolean ran;

最后

看完上述知识点如果你深感 Java 基础不够扎实,或者刷题刷的不够、知识不全面


小编专门为你量身定制了一套<Java 一线大厂高岗面试题解析合集:JAVA 基础-中级-高级面试+SSM 框架+分布式+性能调优+微服务+并发编程+网络+设计模式+数据结构与算法>



针对知识面不够,也莫慌!还有一整套的<Java 核心进阶手册>,可以瞬间查漏补缺



全都是一丢一丢的收集整理纯手打出来的


更有纯手绘的各大知识体系大纲,可供梳理:Java 筑基、MySQL、Redis、并发编程、Spring、分布式高性能架构知识、微服务架构知识、开源框架知识点等等的 xmind 手绘图~




用户头像

还未添加个人签名 2022.04.13 加入

还未添加个人简介

评论

发布
暂无评论
FutureTask的使用方法及实现原理,Java后端路线图_Java_爱好编程进阶_InfoQ写作社区