文档章节

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

0909
 0909
发布于 2017/10/02 18:50
字数 1013
阅读 307
收藏 6
点赞 1
评论 0

最近在看并发包的源码把自己的理解分享给大家啦,有不正确的地方欢迎大家指正。 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方法。有问题欢迎指正。

© 著作权归作者所有

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

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

乒乓狂魔 ⋅ 2016/04/27 ⋅ 1

并发编程之ScheduledThreadPoolExecutor(四)

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

后厂村老司机 ⋅ 05/26 ⋅ 0

深入分析 java 线程池的实现原理

案例1 1、初始化一个包含10个线程的线程池executor; 2、通过方法提交20个任务,每个任务打印当前的线程名; 3、负责执行任务的线程的生命周期都由Executor框架进行管理; ThreadPoolExecut...

xrzs ⋅ 2014/01/14 ⋅ 0

Java并发同步器AQS(AbstractQueuedSynchronizer)学习笔记(2)

学习了[AbstractQueuedSynchronizer][1] 之后(Condition没有在上文做笔记,当应该不难理解),接下来笔者就尝试着分析在JUC包中的各个同步器,其语义是如何实现的。 ReentrantLock 内部类Syn...

zavakid ⋅ 2012/10/25 ⋅ 0

Java线程与多线程教程

Java线程是执行某些任务的轻量级进程。Java通过Thread类提供多线程支持,应用可以创建并发执行的多个线程。 应用中有两类线程——用户线程和守护线程。当启动应用时,main线程是创建的第一个...

LCZ777 ⋅ 2014/03/25 ⋅ 0

深度学习Java Future (一)

作者: 一字马胡 转载标志 【2017-12-07】 更新日志 日期 更新内容 备注 2017-12-07 学习Future的总结 关于Future的深入学习内容 Future 上面这段文字已经说明了Future的本质,一个Future代表...

一字马胡 ⋅ 2017/12/07 ⋅ 0

Java 并发之 AbstractQueuedSynchronizer

如果你读过 JUC 中 ReentrantLock、CountDownLatch、FutureTask、Semaphore 等的源代码,会发现其中都有一个名为 Sync 的类,而这个类是以 AbstractQueuedSynchronizer 为基础的,所以说 Ab...

编走编想 ⋅ 2013/10/30 ⋅ 0

callable future futuretask

1.Callable接口 我们先回顾一下java.lang.Runnable接口,就声明了run(),其返回值为void,当然就无法获取结果了。 [java] view plain copy print? public interface Runnable { public abstr...

hgqxjj ⋅ 2017/11/23 ⋅ 0

源码|使用FutureTask的正确姿势

线程池的实现核心之一是FutureTask。在提交任务时,用户实现的Callable实例task会被包装为FutureTask实例ftask;提交后任务异步执行,无需用户关心;当用户需要时,再调用FutureTask#get()获...

猴子007 ⋅ 2017/11/20 ⋅ 0

Java Executor 框架

Executor框架是指java5中引入的一系列并发库中与executor相关的功能类,包括Executor、Executors、ExecutorService、CompletionService、Future、Callable等。(图片引用自http://www.javacl...

leesama ⋅ 2014/12/01 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

在java中读取文件(也支持读取jar中的文件)

getClass().getResourceAsStream("文件名.格式"); 这个方法是用于获取当前类所在目录下的文件;需要将文件放到和当前类同一个包下面 比如我有个类在 com.test这个包下, 要读取一个test.jpg的图...

太黑_thj ⋅ 46分钟前 ⋅ 0

CentOS 7 源码编译安装 MySQL 5.7记录

没事瞎折腾,本来可以yum安装,却偏偏去要编译源码。 1. 安装依赖包 1). 安装cmake等依赖 # yum install cmake ncurses ncurses-devel bison bison-devel 2). 安装boost 1.59.0 # wget htt...

admin_qing ⋅ 48分钟前 ⋅ 0

tcp/ip详解-链路层

简介 设计链路层的目的: 为IP模块发送和接收IP数据报 为ARP模块发送ARP请求和接收ARP应答 为RARP模块发送RARP请求和接收RARP应答 TCP/IP支持多种链路层协议,如以太网、令牌环往、FDDI、RS-...

loda0128 ⋅ 今天 ⋅ 0

spring.net aop代码例子

https://www.cnblogs.com/haogj/archive/2011/10/12/2207916.html

whoisliang ⋅ 今天 ⋅ 0

发送短信如何限制1小时内最多发送11条短信

发送短信如何限制1小时内最多发送11条短信 场景: 发送短信属于付费业务,有时为了防止短信攻击,需要限制发送短信的频率,例如在1个小时之内最多发送11条短信. 如何实现呢? 思路有两个 截至到当...

黄威 ⋅ 昨天 ⋅ 0

mysql5.7系列修改root默认密码

操作系统为centos7 64 1、修改 /etc/my.cnf,在 [mysqld] 小节下添加一行:skip-grant-tables=1 这一行配置让 mysqld 启动时不对密码进行验证 2、重启 mysqld 服务:systemctl restart mysql...

sskill ⋅ 昨天 ⋅ 0

Intellij IDEA神器常用技巧六-Debug详解

在调试代码的时候,你的项目得debug模式启动,也就是点那个绿色的甲虫启动服务器,然后,就可以在代码里面断点调试啦。下面不要在意,这个快捷键具体是啥,因为,这个keymap是可以自己配置的...

Mkeeper ⋅ 昨天 ⋅ 0

zip压缩工具、tar打包、打包并压缩

zip 支持压缩目录 1.在/tmp/目录下创建目录(study_zip)及文件 root@yolks1 study_zip]# !treetree 11└── 2 └── 3 └── test_zip.txt2 directories, 1 file 2.yum...

蛋黄Yolks ⋅ 昨天 ⋅ 0

聊聊HystrixThreadPool

序 本文主要研究一下HystrixThreadPool HystrixThreadPool hystrix-core-1.5.12-sources.jar!/com/netflix/hystrix/HystrixThreadPool.java /** * ThreadPool used to executed {@link Hys......

go4it ⋅ 昨天 ⋅ 0

容器之上传镜像到Docker hub

Docker hub在国内可以访问,首先要创建一个账号,这个后面会用到,我是用126邮箱注册的。 1. docker login List-1 Username不能使用你注册的邮箱,要用使用注册时用的username;要输入密码 ...

汉斯-冯-拉特 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部