ThreadPoolExecutor submit和execute方法的區(qū)別

結(jié)論

今天先說結(jié)論吧:

  • execute:1.只能提交Runnable接口的對(duì)象,我們知道Runnable接口的run方法是沒有返回值的。2.execute方法提交的任務(wù)異常是直接拋出的
  • submit: 1.可以提交Runnable也可以提交Callable對(duì)象穷蛹,即可以有返回值,也可以沒有践美。2.submit方法是是捕獲了異常的朗兵,當(dāng)調(diào)用FutureTask的get方法時(shí),才會(huì)拋出異常柿隙。
    例子還是在github中

先對(duì)比api

//只接受Runnable對(duì)象沒有返回值
public void execute(Runnable command)

//接受Runnable對(duì)象并返回Future對(duì)象叶洞,調(diào)用Future的get時(shí),正常完成后禀崖,返回null
public Future<?> submit(Runnable task)

//接受Runnable對(duì)象并返回Future對(duì)象衩辟,調(diào)用Future的get時(shí),正常完成后,返回指定的result對(duì)象
public <T> Future<T> submit(Runnable task, T result)

//接受Callable對(duì)象并返回Future對(duì)象波附,調(diào)用Future的get時(shí),正常完成后艺晴,返回task的結(jié)果
public <T> Future<T> submit(Callable<T> task)

execute方法上篇文章已經(jīng)分析過了,這篇我們主要看下submit方法的實(shí)現(xiàn)掸屡,submit三個(gè)方法實(shí)現(xiàn)類似封寞,我們用其中一個(gè)舉例。

public Future<?> submit(Runnable task) {
    //task對(duì)象不能為空
    if (task == null) throw new NullPointerException();
    //將task構(gòu)造成一個(gè)RunnableFuture對(duì)象
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    //調(diào)用execute方法執(zhí)行RunnableFuture對(duì)象
    execute(ftask);
    //返回future對(duì)象
    return ftask;
}

可以看到仅财,submit復(fù)用了execute方法狈究,submit僅僅將傳入的Runnable或者Callable構(gòu)造成一個(gè)RunnableFuture,我們一起看看newTaskFor方法

//構(gòu)造FutureTask返回
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    return new FutureTask<T>(runnable, value);
}

可以看到主要的不同盏求,就是這個(gè)FutureTask抖锥,我們看看FutureTask吧。

public class FutureTask<V> implements RunnableFuture<V> {
    
}

public interface RunnableFuture<V> extends Runnable, Future<V> 

通過上面可以看到FutureTask是實(shí)現(xiàn)了Runnable和Future兩個(gè)接口碎罚,我們知道Future接口提供了cancel磅废,isCancelled,isDone荆烈,get方法还蹲,用于獲取子線程執(zhí)行的結(jié)果。

FutureTask類結(jié)構(gòu)

public class FutureTask<V> implements RunnableFuture<V> {
    /**
     * 可能出現(xiàn)的狀態(tài)路徑
     * NEW -> COMPLETING -> NORMAL
     * NEW -> COMPLETING -> EXCEPTIONAL
     * NEW -> CANCELLED
     * NEW -> INTERRUPTING -> INTERRUPTED
     */
    //任務(wù)執(zhí)行的狀態(tài)
    private volatile int state;
    private static final int NEW          = 0; //初始化狀態(tài)
    private static final int COMPLETING   = 1; //任務(wù)正常結(jié)束或者拋出異常時(shí)的中間態(tài) 這是一個(gè)過渡狀態(tài)
    private static final int NORMAL       = 2; //任務(wù)正常結(jié)束
    private static final int EXCEPTIONAL  = 3; //任務(wù)異常結(jié)束
    private static final int CANCELLED    = 4; //任務(wù)被取消耙考, 注意任務(wù)只有在new狀態(tài)的時(shí)候谜喊,才可以被取消
    private static final int INTERRUPTING = 5; //任務(wù)到中斷狀態(tài)的中間態(tài)
    private static final int INTERRUPTED  = 6; //任務(wù)被中斷

    //執(zhí)行任務(wù)的callable對(duì)象,可以看到傳入的runnable也會(huì)被構(gòu)造成callable
    private Callable<V> callable;
    //正常返回時(shí)的結(jié)果或者任務(wù)拋出的異常
    private Object outcome; // non-volatile, protected by state reads/writes
    //運(yùn)行callable的線程
    private volatile Thread runner;
    //等待的線程節(jié)點(diǎn)倦始,調(diào)用get方法時(shí)斗遏,如果線程沒有運(yùn)行結(jié)束,需要進(jìn)入等待隊(duì)列進(jìn)行等待
    private volatile WaitNode waiters;
    鞋邑。诵次。账蓉。
}

我們上面看到ThreadPoolExecutor調(diào)用的是傳入Runnable的構(gòu)造方法。

public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}
public static <T> Callable<T> callable(Runnable task, T result) {
    if (task == null)
        throw new NullPointerException();
    return new RunnableAdapter<T>(task, result);
}

可以看到構(gòu)造方法中將傳入的Runnable對(duì)象逾一,適配成了RunnableAdapter對(duì)象铸本,另外將state設(shè)置成NEW的狀態(tài)。
通過上篇的分析遵堵,我們知道execute最終執(zhí)行的是Runnable的run方法箱玷,在這里即FutureTask的run方法。

public void run() {
    //狀態(tài)不為初始狀態(tài)或者CAS替換runner為本線程失敗陌宿,都直接返回锡足,說明任務(wù)被別人處理了
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        //將runner設(shè)置為自己后,任務(wù)被自己搶到了
        //任務(wù)不為空且是初始狀態(tài)
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                //調(diào)用Callable對(duì)象的call方法
                result = c.call();
                //正常執(zhí)行完成
                ran = true;
            } catch (Throwable ex) {
                //Callable對(duì)象的call方法拋出了異常
                result = null;
                ran = false;
                //將異常對(duì)象放到outcome屬性中壳坪,標(biāo)記任務(wù)為異常狀態(tài)舶得,喚醒等待的線程
                //這里可以看到對(duì)異常進(jìn)行了捕獲,也就是文章開頭的結(jié)論2
                setException(ex);
            }
            //如果Callable對(duì)象的call方法正常執(zhí)行完成
            //將正常結(jié)果放到outcome屬性中爽蝴,標(biāo)記任務(wù)為正常完成的狀態(tài)沐批,喚醒等待的線程
            if (ran)
                set(result);
        }
    } finally {
        
        runner = null;
        // 再次處理INTERRUPTING狀態(tài)
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

setException 和set方法是兩種不同情況的結(jié)束,但代碼類似蝎亚,我們看看正常結(jié)束的set方法

protected void set(V v) {
    //標(biāo)記state為COMPLETING狀態(tài)
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        //將正常結(jié)果賦值給outcome
        outcome = v;
        //標(biāo)記狀態(tài)為NORMAL(正常結(jié)束)
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        //喚醒等待線程
        finishCompletion();
    }
}

finishCompletion方法

private void finishCompletion() {
    // assert state > COMPLETING;
    //循環(huán)喚起全部的等待線程珠插,后面會(huì)看到其他線程調(diào)用get方法,如果任務(wù)還沒完成會(huì)進(jìn)入等待隊(duì)列
    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
}

前面我們看到set方法或者setException最后都會(huì)進(jìn)行喚醒掛起的線程颖对,現(xiàn)在就一起看看get方法

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    //如果任務(wù)的狀態(tài)為未完成的狀態(tài)捻撑,進(jìn)行等待
    //注意:這里的等待線程和執(zhí)行任務(wù)的線程不是同一個(gè)哦,可以想象主線程new 線程池執(zhí)行任務(wù)缤底,一般都是在主線程中獲取任務(wù)的結(jié)果顾患,就是調(diào)用的get方法
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    //任務(wù)完成以后,根據(jù)任務(wù)的狀態(tài)个唧,返回正常的執(zhí)行結(jié)果江解,或者    
    return report(s);
}

awaitDone方法

private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    WaitNode q = null;
    boolean queued = false;
    for (;;) {
        //響應(yīng)中斷的等待
        if (Thread.interrupted()) {
            removeWaiter(q);
            throw new InterruptedException();
        }

        int s = state;
        //如果任務(wù)已經(jīng)完成直接返回
        if (s > COMPLETING) {
            if (q != null)
                q.thread = null;
            return s;
        }
        // 任務(wù)在中間態(tài),說明應(yīng)該很快就能出結(jié)果徙歼,做一個(gè)讓步犁河,就不在掛起了
        else if (s == COMPLETING) 
            Thread.yield();
        else if (q == null) //構(gòu)造一個(gè)等待節(jié)點(diǎn)
            q = new WaitNode();
        else if (!queued)
            //將等待節(jié)點(diǎn)入隊(duì)
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                 q.next = waiters, q);
        else if (timed) {
            //如果是超時(shí)等待,判斷是否已經(jīng)超時(shí)魄梯,超時(shí)了直接返回
            //沒超時(shí)桨螺,進(jìn)行剩余時(shí)間的掛起(有超時(shí)時(shí)間的掛起是可以自喚醒的)
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
                removeWaiter(q);
                return state;
            }
            LockSupport.parkNanos(this, nanos);
        }
        else
            //掛起自己, 這里就和finishCompletion方法里的LockSupport.unpark對(duì)上了
            LockSupport.park(this);
            //注意:外層無限循環(huán)的操作酿秸,所以串起來就是構(gòu)造一個(gè)等待節(jié)點(diǎn)灭翔,再次進(jìn)入for循環(huán),將節(jié)點(diǎn)進(jìn)入等待隊(duì)列辣苏,再次進(jìn)入for循環(huán)掛起自己肝箱。
            //當(dāng)被喚醒后哄褒,再次進(jìn)入for循環(huán),判斷state> COMPLETING, 方法返回
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末煌张,一起剝皮案震驚了整個(gè)濱河市呐赡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌骏融,老刑警劉巖链嘀,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異绎谦,居然都是意外死亡管闷,警方通過查閱死者的電腦和手機(jī)粥脚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門窃肠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人刷允,你說我怎么就攤上這事冤留。” “怎么了树灶?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵纤怒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我天通,道長(zhǎng)泊窘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任像寒,我火速辦了婚禮烘豹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诺祸。我一直安慰自己携悯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布筷笨。 她就那樣靜靜地躺著憔鬼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胃夏。 梳的紋絲不亂的頭發(fā)上轴或,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音仰禀,去河邊找鬼侮叮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛悼瘾,可吹牛的內(nèi)容都是我干的囊榜。 我是一名探鬼主播审胸,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼卸勺!你這毒婦竟也來了砂沛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤曙求,失蹤者是張志新(化名)和其女友劉穎碍庵,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悟狱,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡静浴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了挤渐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苹享。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖浴麻,靈堂內(nèi)的尸體忽然破棺而出得问,到底是詐尸還是另有隱情,我是刑警寧澤软免,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布宫纬,位于F島的核電站,受9級(jí)特大地震影響膏萧,放射性物質(zhì)發(fā)生泄漏漓骚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一榛泛、第九天 我趴在偏房一處隱蔽的房頂上張望蝌蹂。 院中可真熱鬧,春花似錦挟鸠、人聲如沸叉信。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽硼身。三九已至,卻和暖如春覆享,著一層夾襖步出監(jiān)牢的瞬間佳遂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工撒顿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留丑罪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像吩屹,于是被迫代替她去往敵國(guó)和親跪另。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345