FutureTask源码分析

Java并发工具类的三板斧 状态,队列,CAS

状态:

/** * 当前任务的运行状态。 * * 可能存在的状态转换 * NEW -> COMPLETING -> NORMAL(有正常结果) 
* NEW -> COMPLETING -> EXCEPTIONAL(结果为异常) * NEW -> CANCELLED(无结果) * NEW -> INTERRUPTING -> INTERRUPTED(无结果) */ 
private volatile int state; private static final int NEW = 0; //初始状态 
private static final int COMPLETING = 1; //结果计算完成或响应中断到赋值给返回值之间的状态。 
private static final int NORMAL = 2; //任务正常完成,结果被set 
private static final int EXCEPTIONAL = 3; //任务抛出异常 
private static final int CANCELLED = 4; //任务已被取消 
private static final int INTERRUPTING = 5; //线程中断状态被设置ture,但线程未响应中断 
private static final int INTERRUPTED = 6; //线程已被中断 //将要执行的任务 
private Callable<V> callable; //用于get()返回的结果,也可能是用于get()方法抛出的异常 
private Object outcome; // non-volatile, protected by state reads/writes 
private volatile Thread runner; //执行callable的线程,调用FutureTask.run()方法通过CAS设置 
private volatile WaitNode waiters; //栈结构的等待队列,该节点是栈中的最顶层节点。 

队列:
在FutureTask中,队列的实现是一个单向链表,它表示所有等待任务执行完毕的线程的集合,如果获取结果时,任务还没有执行完毕怎么办呢?那么获取结果的线程就会在一个等待队列中挂起,直到任务执行完毕被唤醒。

挂起的线程什么时候被唤醒?
1.任务执行完毕了,在finishCompletion方法中会唤醒所有在队列中等待的线程
2.等待的线程自身因为被中断等原因而被唤醒。

CAS:
使用CAS来完成入栈出栈操作。为啥要使用一个线程安全的栈呢,因为同一时刻可能有多个线程都在获取任务的执行结果,如果任务还在执行过程中,
则这些线程就要被包装成WaitNode扔到栈的栈顶,即完成入栈操作,这样就有可能出现多个线程同时入栈的情况,因此需要使用CAS操作保证入栈的线程安全,对于出栈的情况也是同理。
使用这个队列就只需要一个指向栈顶节点的指针就行了

    /**
     * @throws CancellationException {@inheritDoc}
     */
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            //如果状态小于COMPLETING  说明还没计算完成,则调用 awaitDone方法阻塞
            s = awaitDone(false, 0L);
        return report(s);
    }


    private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }


 private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
            if (Thread.interrupted()) {
                // 如果该线程被中断,则从等待队列中移除该线程,直接抛异常
                removeWaiter(q);
                throw new InterruptedException();
            }

            int s = state;
            if (s > COMPLETING) {
                // 如果state >COMPLETING 说明任务已经执行完成,或者取消,如果等待队列不为空,将等待队列中该节点置为null,
                // 返回state 然后就可以在外层方法调用report方法获取结果了
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (s == COMPLETING) // cannot time out yet
              //如果state=COMPLETING,则使用yield,因为此状态的时间特别短,通过yield比挂起响应更快。
              //yield 表示让出CPU执行权,等待下一次竞争。
                Thread.yield();
            else if (q == null)
                q = new WaitNode();
            else if (!queued)
                // 如果q不为null,说明代表当前线程的WaitNode已经被创建出来了,如果queued=false,表示当前线程还没有入队 接下来利用CAS入队
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,q.next = waiters, q);
            //如果需要阻塞指定时间,则使用LockSupport.parkNanos阻塞指定时间                    
            else if (timed) {
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                     //如果到指定时间还没执行完,则从队列中移除该节点,并返回当前状态           
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
              
               
        }
    }


public void run() {
    // 利用CAS设置当前执行线程,保证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;
                    setException(ex);
                }
                if (ran)
                    // 设置结果
                    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);
        }
    }

      protected void set(V v) {
        // 先利用CAS将状态改为COMPLETING 保证只有一个线程能够执行  outcome = v;
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            // 将结果赋值给 outcome
            outcome = v;
             // 再利用CAS将状态改为NORMAL
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            // 将队列清空,然后唤醒队列中所有线程
            finishCompletion();
        }
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容