文档章节

从FutureTask内部类WaitNode深入浅出分析FutureTask实现原理

叶易
 叶易
发布于 2017/10/02 18:50
字数 1013
阅读 308
收藏 6

最近在看并发包的源码把自己的理解分享给大家啦,有不正确的地方欢迎大家指正。 FutureTask类中的waiters成员变量保存着调用get方法获取FutureTask计算结果的线程构成的一个栈。 当FutureTask类run方法没有执行完时,调用get方法的线程会形成一个阻塞的栈,即waiters。

static final class WaitNode {
        volatile Thread thread;
        volatile WaitNode next;
        WaitNode() { thread = Thread.currentThread(); }
  }

一旦FutureTask类run方法执行完、执行中出现异常或者是调用cancel方法取消执行(可以通知正在执行 FutureTask类run方法的线程响应中断,通过设置cancel方法参数mayInterruptIfRunning为true达到) 时,就必须让阻塞在waiters栈的所有线程退出阻塞。这个是通过FutureTask类的finishCompletion方法 完成的。源码如下:

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 gc
                q = next;
            }
            break;
        }
    }
    done();
    callable = null;        // to reduce footprint
}

所以,在FutureTask类run方法中的执行完成员变量的callable的call方法时,正常执行或是执行出现异常 调用set设置执行后的结果或是setException设置执行返回出现的异常时,其内部都调用finishCompletion 方法,同理cancel方法内部也调用了finishCompletion方法。FutureTask类run方法源码如下:

public void run() {
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                /*执行callable的call方法出现异常,设置异常,setException内部调用
                   finishCompletion让阻塞在waiters栈上的所有要获取run方法执行结
                   果的线程全部停止阻塞,得到执行时的异常
                */
                setException(ex);
            }
            if (ran)
                /*正常执行完callable的call方法,设置返回结果,set内部调用finishCompletion
                   让阻塞在waiters栈上的所有要获取run方法执行结果的线程全部停止阻塞得到
                   执行的结果
                */
                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 interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

现在反过来分析一下,waiters栈是如何构成的。先看一下FutureTask类get方法,就是得到Callable 的执行结果,我们前说的所有要获取run方法执行结果的线程,就是调用 get方法。源码如下:

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)
        /*FutureTask类run方法执行callable的call方法没完成,要获取run方法执行结果的线程阻塞构成
           waiters栈
        */
        s = awaitDone(false, 0L);
    return report(s);
}

FutureTask类有个get变体用于在指定时间没有获取结果抛出异常,同get类似这里不做分析,来看下 awaitDone源码:

/**
* Awaits completion or aborts on interrupt or timeout.
*
* [@param](https://my.oschina.net/u/2303379) timed true if use timed waits
* [@param](https://my.oschina.net/u/2303379) nanos time to wait, if timed
* [@return](https://my.oschina.net/u/556800) state upon completion
*/
private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    WaitNode q = null;
    boolean queued = false;
   //自旋
    for (;;) {
        //调用get的线程被中断,将其对应waiterNode移除,同时抛出异常,interrupted会重置中断状态
        if (Thread.interrupted()) {
            removeWaiter(q);
            throw new InterruptedException();
        }
        int s = state;
        //FutureTask中run方法执行,即callable任务执行完,返回执行的状态
        if (s > COMPLETING) {
            if (q != null)
                q.thread = null;
            return s;
        }
        //FutureTask执行处于中间状态,获取结果的线程将cup执行机会让给真正要执行FutureTask类run方法的线程
        else if (s == COMPLETING) // cannot time out yet
            Thread.yield();
        else if (q == null)
        //将要获取结果的线程封装成对应的WaitNode,用于后面构建阻塞waiters栈
            q = new WaitNode();
        else if (!queued)
            //构建阻塞waiters栈
            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
            //阻塞当前获取FutureTask类执行结果的线程
            LockSupport.park(this);
    }
}

上面注解已详细分析了awaitDone方法。有问题欢迎指正。

© 著作权归作者所有

共有 人打赏支持
叶易
粉丝 22
博文 23
码字总数 30565
作品 0
无锡
线程池源码分析-FutureTask

1 系列目录 - 线程池接口分析以及FutureTask设计实现- 线程池源码分析-ThreadPoolExecutor 该系列打算从一个最简单的Executor执行器开始一步一步扩展到ThreadPoolExecutor,希望能粗略的描述...

乒乓狂魔
2016/04/27
613
1
java并发编程——FutureTask源码分析

FutureTask的简单示例: FutureTask的应用场景,如果在当前线程中需要执行比较耗时的操作,但又不想阻塞当前线程时,可以把这些作业交给FutureTask,另开一个线程在后台完成,当当前线程将来...

长头发-dawn
09/07
0
0
并发编程之ScheduledThreadPoolExecutor(四)

一、ScheduledThreadPoolExecutor的爷爷类AbstractExecutorService 本篇把上一篇漏掉的一些如submit等方法的解析补回来,尽量给大家构建一个完整的体系。想要了解submit首先得了解FutureTas...

后厂村老司机
05/26
0
0
Java并发编程原理与实战三十一:Future&FutureTask 浅析

一、Futrue模式有什么用? ------>正所谓技术来源与生活,这里举个栗子。在家里,我们都有煮菜的经验。(如果没有的话,你们还怎样来泡女朋友呢?你懂得)。现在女票要你煮四菜一汤,这汤是鸡...

pony1223
08/19
0
0
Callable :彻底理解Java的Future模式

先上一个场景:假如你突然想做饭,但是没有厨具,也没有食材。网上购买厨具比较方便,食材去超市买更放心。 实现分析:在快递员送厨具的期间,我们肯定不会闲着,可以去超市买食材。所以,在...

勇恒
07/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

你为什么在Redis里读到了本应过期的数据

一个事故的故事 晚上睡的正香突然被电话吵醒,对面是开发焦急的声音:我们的程序在访问redis的时候读到了本应过期的key导致整个业务逻辑出了问题,需要马上解决。 看到这里你可能会想:这是不...

IT--小哥
今天
2
0
祝大家节日快乐,阖家幸福! centos GnuTLS 漏洞

yum update -y gnutls 修复了GnuTLS 漏洞。更新到最新 gnutls.x86_64 0:2.12.23-22.el6 版本

yizhichao
昨天
5
0
Scrapy 1.5.0之选择器

构造选择器 Scrapy选择器是通过文本(Text)或 TextResponse 对象构造的 Selector 类的实例。 它根据输入类型自动选择最佳的解析规则(XML vs HTML): >>> from scrapy.selector import Sele...

Eappo_Geng
昨天
4
0
Windows下Git多账号配置,同一电脑多个ssh-key的管理

Windows下Git多账号配置,同一电脑多个ssh-key的管理   这一篇文章是对上一篇文章《Git-TortoiseGit完整配置流程》的拓展,所以需要对上一篇文章有所了解,当然直接往下看也可以,其中也有...

morpheusWB
昨天
5
0
中秋快乐!!!

HiBlock
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部