• 【JUC源码解析】FutureTask


    简介

    FutureTask, 一个支持取消行为的异步任务执行器。

    概述

    FutureTask实现了Future,提供了start, cancel, query等功能,并且实现了Runnable接口,可以提交给线程执行。

    源码分析

    状态

    1     private volatile int state; // 执行器状态
    2     private static final int NEW = 0; // 初始值
    3     private static final int COMPLETING = 1; // 完成进行时
    4     private static final int NORMAL = 2; // 正常结束
    5     private static final int EXCEPTIONAL = 3; // 发生异常
    6     private static final int CANCELLED = 4; // 已经取消
    7     private static final int INTERRUPTING = 5; // 中断进行时
    8     private static final int INTERRUPTED = 6; // 中断结束

    转换

    NEW -> COMPLETING -> NORMAL
    NEW -> COMPLETING -> EXCEPTIONAL
    NEW -> CANCELLED
    NEW -> INTERRUPTING -> INTERRUPTED

    属性

    1     private Callable<V> callable; // 任务
    2     private Object outcome; // 返回结果
    3     private volatile Thread runner; // 执行线程
    4     private volatile WaitNode waiters; // 等待线程们

    run()

     1     public void run() {
     2         // UNSAFE.compareAndSwapObject, CAS保证Callable任务只被执行一次
     3         if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
     4             return;
     5         try {
     6             Callable<V> c = callable; // 拿到执行任务
     7             if (c != null && state == NEW) { // 任务不为空,并且执行器状态是初始值,才会执行;如果取消就不执行了
     8                 V result;
     9                 boolean ran; // 记录是否执行成功
    10                 try {
    11                     result = c.call(); // 执行任务
    12                     ran = true; // 成功
    13                 } catch (Throwable ex) {
    14                     result = null; // 异常,清空结果
    15                     ran = false; // 失败
    16                     setException(ex); // 记录异常
    17                 }
    18                 if (ran) // 问题:ran变量可以省略吗,把set(result);移到try块里面?
    19                     set(result); // 设置结果
    20             }
    21         } finally {
    22             runner = null; // 直到set状态前,runner一直都是非空的,为了防止并发调用run()方法。
    23             int s = state;
    24             if (s >= INTERRUPTING) // 有别的线程要中断当前线程,把CPU让出去,自旋等一下
    25                 handlePossibleCancellationInterrupt(s);
    26         }
    27     }
    1.  检查state,非NEW,说明已经启动,直接返回;否则,设置runner为当前线程,成功则继续,否则,返回。
    2. 调用Callable.call()方法执行任务,成功则调用set(result)方法,失败则调用setException(ex)方法,最终都会设置state,并调用finishCompletion()方法,唤醒阻塞在get()方法上的线程们。
    3. 接2,如注释所示,如果省略ran变量,并把"set(result);" 语句移动到try代码块"ran = true;" 语句处,会怎样呢?首先,从代码逻辑上看,是没有问题的,但是,考虑到"set(result);"方法万一抛出异常甚至是错误了呢?set()方法最终会调用到用户自定义的done()方法,所以,不可省略。
    4. 如果state为INTERRUPTING, 则主动让出CPU,自旋等待别的线程执行完中断流程。见handlePossibleCancellationInterrupt(int s) 方法。

     set(V)

    1     protected void set(V v) {
    2         if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { // CAS state NEW -> COMPLETING
    3             outcome = v; // 将结果赋值给outcome
    4             UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // 然后将state修改为NORMAL,表示现在可以获取返回结果
    5             finishCompletion(); // 唤醒等待的所有线程
    6         }
    7     }

    setException(Throwable)

    1     protected void setException(Throwable t) {
    2         if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { // CAS state NEW -> COMPLETING
    3             outcome = t; // 将结果赋值给outcome
    4             UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // 然后将state修改为EXCEPTIONAL,表示现在可以获取返回异常信息
    5             finishCompletion(); // 唤醒等待的所有线程
    6         }
    7     }

    handlePossibleCancellationInterrupt(int)

    1     private void handlePossibleCancellationInterrupt(int s) {
    2         if (s == INTERRUPTING) // 当state为INTERRUPTING时
    3             while (state == INTERRUPTING) // 表示有线程正在中断当前线程
    4                 Thread.yield(); // 让出CPU,自旋等待中断
    5     }

    finishCompletion()

     1     private void finishCompletion() {
     2         for (WaitNode q; (q = waiters) != null;) {
     3             if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) { // CAS waiters -> null,避免重复
     4                 for (;;) {
     5                     Thread t = q.thread; // 获得结点对应的线程
     6                     if (t != null) { // 如果不为空
     7                         q.thread = null; // 复位thread属性
     8                         LockSupport.unpark(t); // 唤醒此线程
     9                     }
    10                     WaitNode next = q.next; // 取得下一个结点
    11                     if (next == null) // 如果为空则跳出内层循环,继而跳出外层循环
    12                         break;
    13                     q.next = null; // 复位next属性
    14                     q = next; // 移动到下一个结点
    15                 }
    16                 break; // 跳出外层循环
    17             }
    18             // 如果CAS失败,说明waiters发生变化,则重试,直至CAS成功,或者waiters为null(别的线程已经完成此操作)
    19         }
    20 
    21         done(); // 调用done()方法,hook钩子,子类实现
    22 
    23         callable = null; // 复位callable,其所表示的任务已结束
    24     }

    将结点一个一个地弹出,并唤醒其所持的线程。

    WaitNode

    1     static final class WaitNode {
    2         volatile Thread thread; // 线程
    3         volatile WaitNode next; // 指向下一个结点
    4 
    5         WaitNode() {
    6             thread = Thread.currentThread(); // 当前线程
    7         }
    8     }

    runAndReset()

     1     protected boolean runAndReset() {
     2         // UNSAFE.compareAndSwapObject, CAS保证Callable任务只被执行一次
     3         if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
     4             return false;
     5         boolean ran = false; // 记录是否执行成功
     6         int s = state; // 状态
     7         try {
     8             Callable<V> c = callable; // 任务
     9             if (c != null && s == NEW) { // 任务不为空,并且执行器状态是初始值,才会执行;如果取消就不执行了
    10                 try {
    11                     c.call(); // 不设置结果,可重复执行
    12                     ran = true;
    13                 } catch (Throwable ex) {
    14                     setException(ex); // 直至遇到异常
    15                 }
    16             }
    17         } finally {
    18             runner = null; // 置空,其他线程可以继续执行
    19             s = state; // 状态
    20             if (s >= INTERRUPTING) // 有别的线程要中断当前线程,把CPU让出去,自旋等一下
    21                 handlePossibleCancellationInterrupt(s);
    22         }
    23         return ran && s == NEW; // 返回结果,执行成功,并且,state为NEW,才返回true
    24     }

    get()

    1     public V get() throws InterruptedException, ExecutionException {
    2         int s = state; // 执行器状态
    3         if (s <= COMPLETING) // 如果状态小于等于COMPLETING,说明任务正在执行,需要等待
    4             s = awaitDone(false, 0L); // 等待
    5         return report(s); // 报告结果
    6     }

    get(long, TimeUnit)

    1     public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
    2         if (unit == null) // 参数校验
    3             throw new NullPointerException();
    4         int s = state; // 执行器状态
    5         if (s <= COMPLETING && (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING) // 如果状态小于等于COMPLETING,说明任务正在执行,需要等待;等待指定时间,state依然小于等于COMPLETING
    6             throw new TimeoutException(); // 抛出超时异常
    7         return report(s); // 报告结果
    8     }

    report(int)

    1     private V report(int s) throws ExecutionException {
    2         Object x = outcome; // 返回结果
    3         if (s == NORMAL) // 如果state为NORMAL,表示任务正常结束,返回结果
    4             return (V) x;
    5         if (s >= CANCELLED) // 如果任务大于等于CANCELLED,表示任务已经取消,抛出取消异常
    6             throw new CancellationException();
    7         throw new ExecutionException((Throwable) x); // 否则,抛出执行异常
    8     }

    awaitDone(boolean timed, long nanos)

     1     private int awaitDone(boolean timed, long nanos) throws InterruptedException {
     2         final long deadline = timed ? System.nanoTime() + nanos : 0L; // 计算deadline
     3         WaitNode q = null; // 等待结点
     4         boolean queued = false; // 是否已经入队
     5         for (;;) {
     6             if (Thread.interrupted()) { // 如果当前线程已经标记中断,则直接移除此结点,并抛出中断异常
     7                 removeWaiter(q);
     8                 throw new InterruptedException();
     9             }
    10 
    11             int s = state; // 执行器状态
    12             if (s > COMPLETING) { // 如果状态大于COMPLETING,说明任务已经完成,或者已经取消,直接返回
    13                 if (q != null)
    14                     q.thread = null; // 复位线程属性
    15                 return s; // 返回
    16             } else if (s == COMPLETING) // 如果状态等于COMPLETING,说明正在整理结果,自旋等待一会儿
    17                 Thread.yield();
    18             else if (q == null) // 初始,构建结点
    19                 q = new WaitNode();
    20             else if (!queued) // 还没入队,则CAS入队
    21                 queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
    22             else if (timed) { // 是否允许超时
    23                 nanos = deadline - System.nanoTime(); // 计算等待时间
    24                 if (nanos <= 0L) { // 超时
    25                     removeWaiter(q); // 移除结点
    26                     return state; // 返回结果
    27                 }
    28                 LockSupport.parkNanos(this, nanos); // 线程阻塞指定时间
    29             } else
    30                 LockSupport.park(this); // 阻塞线程
    31         }
    32     }

    removeWaiter(WaitNode)

     1     private void removeWaiter(WaitNode node) {
     2         if (node != null) { // 移除结点的标准是thread属性已经为null
     3             node.thread = null; // 每个线程对应一个结点,当需要移除时,仅仅需要将thread属性置为null即可,此过程可并发执行
     4             retry: for (;;) { // 然而,移除操作需要穿行协作,一有变动,须重试
     5                 for (WaitNode pred = null, q = waiters, s; q != null; q = s) { // 记录前驱结点,当前结点,以及后继结点
     6                     s = q.next; // 后继结点
     7                     if (q.thread != null) // 不为null, 前驱指向当前结点,即整体右移,左边的结点是安全的。表示不必删除
     8                         pred = q;
     9                     else if (pred != null) { // 如果当前线程为空,前驱不为null
    10                         pred.next = s; // 前驱越过当前结点指向后继结点,等于把当前结点移除了
    11                         if (pred.thread == null) // 如果前驱结点thread属性也为null,则很可能任务完成,线程unpark了,需要从头检查一下。
    12                             continue retry;
    13                     } else if (!UNSAFE.compareAndSwapObject(this, waitersOffset, q, s)) // CAS失败,waiters变动,重试
    14                         continue retry;
    15                 }
    16                 break; // 跳出
    17             }
    18         }
    19     }

    cancel(boolean)

     1     public boolean cancel(boolean mayInterruptIfRunning) {
     2         // 状态不为NEW,或者CAS NEW -> INTERRUPTING/CANCELLED, 直接返回
     3         if (!(state == NEW
     4                 && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
     5             return false;
     6         try {
     7             if (mayInterruptIfRunning) { // 中断
     8                 try {
     9                     Thread t = runner;
    10                     if (t != null)
    11                         t.interrupt(); // 中断此线程
    12                 } finally {
    13                     UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // state设置为INTERRUPTED
    14                 }
    15             }
    16         } finally {
    17             finishCompletion(); // 唤醒阻塞线程
    18         }
    19         return true;
    20     }

    至此,结束。

      

    尊重他人的劳动,转载请注明出处:http://www.cnblogs.com/aniao/p/aniao_futuretask.html

  • 相关阅读:
    C# WinForms多线程编程-摇奖程序
    C#多线程编程实例介绍
    C#多线程编程(1) —— 多线程与UI操作
    C#引用类型与值类型浅析
    HTML中空格占位符的几种方式
    C#中字符串排序的问题和解决方法
    InstanceContextMode和ConcurrencyMode的默认值
    The "IsFileSystemCaseSensitive" parameter is not supported by the "FindConfigFiles" task
    jQuery中delegate() 和 on()的出现版本
    NHibernate 分页优化,针对SQLServer(未深入测试)
  • 原文地址:https://www.cnblogs.com/aniao/p/aniao_futuretask.html
Copyright © 2020-2023  润新知