成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

硬核詳解 FutureTask 設(shè)計與實現(xiàn)

開發(fā)
本文將從實踐和源碼兩個角度分析 FutureTask 的設(shè)計與實現(xiàn)思路,希望對你有幫助。

最近看到一篇比較不錯的FutureTask實踐,于是對FutureTask源碼進行了研究,而本文將從實踐和源碼兩個角度分析FutureTask的設(shè)計與實現(xiàn)思路,希望對你有幫助。

FutureTask使用示例

我們的批量線程會進行嘗試創(chuàng)建一些任務(wù)執(zhí)行,同時我們希望每個任務(wù)只有有一個線程去執(zhí)行,其他線程如果拿到這個任務(wù)準(zhǔn)備執(zhí)行時發(fā)現(xiàn)這個任務(wù)已經(jīng)在執(zhí)行,則等待這個任務(wù)的返回結(jié)果。

如下圖,假設(shè)我們的第一個線程提交task-1至清單成功后,這個線程就會執(zhí)行該任務(wù),而線程2同樣想提交這個任務(wù)發(fā)現(xiàn)該任務(wù)已存在,則直接等待清單中記錄的這個任務(wù)的結(jié)果返回。而線程3則也是因為第一次提交任務(wù),所以提交清單成功并執(zhí)行。

我們完全可以通過FutureTask做到這一點,先來說說任務(wù)清單,它用于存儲正在執(zhí)行的任務(wù)和任務(wù)名,為了保證多線程并發(fā)操作安全,筆者直接采用ConcurrentHashMap:

//保存執(zhí)行的任務(wù)清單
    private static ConcurrentHashMap<String, Future<String>> taskCache = new ConcurrentHashMap<>();

每一個線程執(zhí)行任務(wù)則都是通過executionTask方法,該邏輯一旦檢查任務(wù)不存在則創(chuàng)建,然后通過樂觀鎖方式保證任務(wù)不存在時才能提交,完成這些操作后通過get等待結(jié)果返回。 需要注意的是,這里筆者為了保證邏輯可用做了一點小小的計數(shù)處理,每當(dāng)一個任務(wù)通過run執(zhí)行時,筆者會用原子類自增一下,以此判斷多線程執(zhí)行該方法時有沒有重復(fù)執(zhí)行任務(wù)的情況出現(xiàn)。

privatestatic AtomicInteger counter = new AtomicInteger(0);


public static String executionTask(String taskName) {

       while (true){//執(zhí)行到任務(wù)成功
           if (!taskCache.containsKey(taskName)) {//如果任務(wù)不存在則創(chuàng)建任務(wù)

               Callable<String> callable = () -> taskName;
               FutureTask<String> futureTask = new FutureTask<>(callable);

               //雙重鎖校驗避免任務(wù)重復(fù)提交
               Future<String> future = taskCache.putIfAbsent(taskName, futureTask);
               if (future == null) {//如果返回空則說明這個任務(wù)之前沒有提交過,當(dāng)前線程直接執(zhí)行
                   futureTask.run();
                   //累加執(zhí)行的任務(wù)數(shù)
                   counter.incrementAndGet();
               }
           }

           //任務(wù)非空或提交任務(wù)完成的線程在這里等待任務(wù)返回
           try {
               return taskCache.get(taskName).get();
           } catch (Exception e) {
               //如果保存則從清單中移除
               taskCache.remove(taskName);
           }
       }

    }

對應(yīng)的測試代碼如下,筆者提交10w個線程執(zhí)行10個任務(wù),從計數(shù)器的輸出結(jié)果來看,執(zhí)行了10個任務(wù),沒有問題:

public static void main(String[] args) throws Exception {
        //創(chuàng)建線程池
        ExecutorService executorService = Executors.newFixedThreadPool(10_000);



        for (int i = 0; i < 10_000; i++) {
            //通過取模運算保證10w個線程只會創(chuàng)建 名為10以內(nèi)的任務(wù)
            int finalI = i % 10;
            //提交任務(wù)
            executorService.submit(() -> FutureTaskExample.executionTask(String.valueOf(finalI)));

        }

        //等待結(jié)束
        executorService.shutdown();
        while (!executorService.isTerminated()) {

        }
        //查看計數(shù)
        System.out.println(counter.get());//10

    }

FutureTask在閉鎖下的哲學(xué)

本質(zhì)上FutureTask也可以是一種閉鎖,即在FutureTask對應(yīng)線程未完成運算前,F(xiàn)utureTask這個閉鎖就像一個大門一樣不允許所有線程通過,只有FutureTask完成運算進入完成狀態(tài)后,其它線程才能通過。

例如我們希望通過FutureTask執(zhí)行一些耗時的運算,此時就可以:

  • 通過FutureTask提交任務(wù)
  • 異步任務(wù)運算期間,執(zhí)行一些其它任務(wù)
  • 通過get阻塞等待FutureTask結(jié)果返回
  • FutureTask任務(wù)返回線程通過,打印輸出結(jié)果

所以FutureTask也是一個高效的異步工具,我們可以將一些耗時的操作提前啟動,著手其它耗時操作等待完成后拿結(jié)果:

對應(yīng)的我們也給出上述實現(xiàn)的代碼示例:

public class Task {
    //休眠完成后,返回一個隨機數(shù)
    privatefinal FutureTask<Integer> futureTask = new FutureTask<>(() -> {
        ThreadUtil.sleep(1000);
        return RandomUtil.randomInt();
    });

    privatefinal Thread thread = new Thread(futureTask);



    //啟動任務(wù)執(zhí)行
    public void start() {
        thread.start();
    }


    //阻塞獲取結(jié)果
    public int get() throws ExecutionException, InterruptedException {
        return futureTask.get();
    }

}

使用實例如下,即通過FutureTask執(zhí)行異步運算后,通過get執(zhí)行閉鎖控制異步流程:

Task task = new Task();

        long begin = System.currentTimeMillis();
        task.start();

        //futureTask異步運行期間,執(zhí)行一些業(yè)務(wù)邏輯
        System.out.println("do something...");

        //阻塞等待futureTask完成,即利用get方法實現(xiàn)一個閉鎖的操作
        int result = task.get();
        long end = System.currentTimeMillis();
        Console.log("result: {},cost:{}ms", result, end - begin);

輸出結(jié)果如下,可以看到整體來說利用了異步運算期間執(zhí)行一些其它操作,同時還使用get保證整體流程順序正常:

do something...
result: -1158881871,cost:1009ms

FutureTask狀態(tài)機的扭轉(zhuǎn)

在正式進行源碼介紹之前,筆者先簡單介紹一下FutureTask執(zhí)行狀態(tài)的扭轉(zhuǎn),F(xiàn)utureTask在創(chuàng)建時是全新的任務(wù),此時它的狀態(tài)就是NEW,一旦調(diào)用run之后就會開始運行,此時就會出現(xiàn)4個分支:

  • 當(dāng)任務(wù)正確執(zhí)行完成之后,先將狀態(tài)設(shè)置為完成中COMPLETING,然后將執(zhí)行結(jié)果存入outcome變量中,完成后再將狀態(tài)設(shè)置為正常結(jié)束,即NORMAL。
  • 一旦任務(wù)執(zhí)行出現(xiàn)異常,F(xiàn)utureTask則同樣將任務(wù)設(shè)置為完成中,將結(jié)果設(shè)置為null之后,調(diào)整狀態(tài)為執(zhí)行出錯EXCEPTIONAL。
  • 當(dāng)任務(wù)在執(zhí)行過程中,需要進行打斷操作時,F(xiàn)utureTask會將狀態(tài)設(shè)置為打斷中INTERRUPTING,一旦線程正常被打斷,任務(wù)就會被設(shè)置為終態(tài)INTERRUPTED。
  • FutureTask同樣支持不打斷當(dāng)前執(zhí)行線程,這一點筆者會在后文中說明,這種情況則直接將線程設(shè)置為CANCELLED。

一旦狀態(tài)按照預(yù)期調(diào)整為終態(tài)后,F(xiàn)utureTask就會喚醒那些等待任務(wù)執(zhí)行完成的線程:

這4種狀態(tài)扭轉(zhuǎn),我們也可以通過閱讀FutureTask上關(guān)于狀態(tài)變量的注釋了解:

/*
     * Possible state transitions:
     * NEW -> COMPLETING -> NORMAL
     * NEW -> COMPLETING -> EXCEPTIONAL
     * NEW -> CANCELLED
     * NEW -> INTERRUPTING -> INTERRUPTED
     */
    privatevolatileint state;
    privatestaticfinalint NEW          = 0;
    privatestaticfinalint COMPLETING   = 1;
    privatestaticfinalint NORMAL       = 2;
    privatestaticfinalint EXCEPTIONAL  = 3;
    privatestaticfinalint CANCELLED    = 4;
    privatestaticfinalint INTERRUPTING = 5;
    privatestaticfinalint INTERRUPTED  = 6;

FutureTask如何執(zhí)行

FutureTask在創(chuàng)建之初時的初始態(tài)為NEW,也就是整數(shù)變量0,一旦某個線程執(zhí)行這個任務(wù),JDK8版本會通過原子操作將記錄運行線程的地址(這個位置的偏移量用runnerOffset記錄)設(shè)置為當(dāng)前線程,一旦操作成功則執(zhí)行該task封裝的Callable任務(wù),如果運行成功則將result設(shè)置為運行后的結(jié)果,并將ran標(biāo)志為true,并將任務(wù)狀態(tài)設(shè)置為NORMAL:

反之如果運行失敗,則將ran設(shè)置為false,result設(shè)置為空,并將錯誤信息設(shè)置到stateOffset標(biāo)志位上了,將任務(wù)運行狀態(tài)設(shè)置為終態(tài)EXCEPTIONAL,然后喚醒其他需要處理這個任務(wù)的線程:

對此我們給出FutureTask的底層實現(xiàn),可以看到FutureTask只有狀態(tài)為NEW且通過CAS操作將runner設(shè)置為自己的線程才能執(zhí)行任務(wù),而后續(xù)的線程如果看到state不為new則只能獲取結(jié)果,而不能執(zhí)行,這就FutureTask避免重復(fù)運行的核心設(shè)計所在。 進行樂觀鎖上鎖拿到執(zhí)行權(quán)之后,就會基于CAS上鎖的線程進行任務(wù)調(diào)用,對應(yīng)的結(jié)果扭轉(zhuǎn)就如上文所說,這里讀者可以參考筆者注釋自行閱讀:

public void run() {
//1. 為NEW 說明是第一次運行,則可以通過CAS操作獲取執(zhí)行權(quán)
//2. 不為NEW,直接返回
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            //如果任務(wù)不為空且狀態(tài)為NEW則調(diào)用call運行
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                 //運行成功則記錄結(jié)果到result 并將ran 設(shè)置為true
                    result = c.call();    
                    ran = true;
                } catch (Throwable ex) {
                //如果報錯則result設(shè)置為空,并將ran設(shè)置為false
                    result = null;
                    ran = false;
                    //并將任務(wù)狀態(tài)設(shè)置為錯誤
                    setException(ex);
                }
                //如果運行成功則將結(jié)果存到outcome中
                if (ran)
                    set(result);
            }
        } finally {
            //......
        }
    }

這里我們直接查看獲取結(jié)果成功后的狀態(tài)設(shè)置方法set方法,可以看到其內(nèi)部先通過cas將status即狀態(tài)字段設(shè)置為COMPLETING,完成結(jié)果設(shè)置之后,再通過putOrderedInt將狀態(tài)設(shè)置為終態(tài)NORMAL,這么做的原因是為什么呢?

protected void set(V v) {
  //先CAS設(shè)置為完成中
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
         //設(shè)置結(jié)果完成后,才能設(shè)置狀態(tài)為正常結(jié)束
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }

多線程執(zhí)行FutureTask情況下,大部分判斷都需要依賴于COMPLETING這個中間態(tài)(利用get和COMPLETING方法),所以這個狀態(tài)的可見性要求相對高一些,所以在進行結(jié)果設(shè)置之前,先通過CAS的方式進行更新status字段狀態(tài),這種操作是需要將storeLoad屏障,雖然性能表現(xiàn)差一些,但可以保證可見性和有序性,所以先通過這個操作保證其他線程對于這個中態(tài)狀態(tài)可見保證并發(fā)操作一致性。

然后完成任務(wù)處理結(jié)果設(shè)置,此時在邏輯上我們可以認(rèn)定這個任務(wù)是處理完成了,因為大部分的邏輯判斷都是依賴于COMPLETING,對于終態(tài)(NORMAL、EXCEPTIONAL、CANCELLED、INTERRUPTED)的可見性要求不高,所以FutureTask的設(shè)計者直接采用putOrderedInt這種操作保證寫入不會被重排序,但不會立即刷到一致性內(nèi)存行上,所以在性能表現(xiàn)上會出色一些。

上述對于正確處理結(jié)果的設(shè)置,可以在set這個方法的源碼上得以印證,讀者可以結(jié)合上文筆者所說和注釋自行了解這段邏輯:

protected void set(V v) {
 //通過cas操作volatile變量status完成中態(tài)設(shè)置,又保證的多核心可見性
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
         //設(shè)置處理結(jié)果到outcome上,后續(xù)其他線程get都是從這個outcome變量上獲取
            outcome = v;
            //因為終態(tài)可見性要求不高,所以通過putOrderedInt設(shè)置終態(tài),保證寫入有序性
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            //喚醒其他線程處理當(dāng)前任務(wù)
            finishCompletion();
        }
    }

多線程如何獲取FutureTask執(zhí)行結(jié)果

其他線程需要從get方法獲取結(jié)果時,其內(nèi)部本質(zhì)就是調(diào)用awaitDone等待完成,假設(shè)我們用putOrderedInt寫入,且狀態(tài)對于當(dāng)前線程不可見,那么這個線程要做的也僅僅是yield讓出處理器的使用權(quán),相比之下volatile寫這種需要增加內(nèi)存屏障寫入的開銷,這種內(nèi)存消耗無論從概率還是消耗上,都是劃得來的。

最后完成上述操作,通過finishCompletion通知其他的等待線程可以開始處理FutureTask了。

對此我們也給出get的源碼和注釋,讀者可結(jié)合上文感知一下邏輯:

public V get() throws InterruptedException, ExecutionException {
        int s = state;
        //狀態(tài)在COMPLETING及其之前調(diào)用awaitDone等待完成
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        finallong deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
          //......
          //如果狀態(tài)處于中態(tài)完成中,則讓出線程對于處理器的使用權(quán)
            elseif (s == COMPLETING) // cannot time out yet
                Thread.yield();
//......
}

我們給出狀態(tài)設(shè)置為終態(tài)之后,finishCompletion的邏輯,比較簡單,可以看到它僅是獲取當(dāng)前等待節(jié)點的后一個線程的WaitNode ,通過unpark將其喚醒,然后獲取其后繼節(jié)點繼續(xù)進行喚醒操作:

private void finishCompletion() {
        // assert state > COMPLETING;
        //獲取后繼節(jié)點
        for (WaitNode q; (q = waiters) != null;) {
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                for (;;) {
                //將該等待節(jié)點直接喚醒
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    //指向已喚醒節(jié)點的后繼節(jié)點
                    WaitNode next = q.next;
                    //若為空直接退出
                    if (next == null)
                        break;
                    //將后繼指針設(shè)置為空,輔助gc,并讓q指向后續(xù)節(jié)點,繼續(xù)進行喚醒操作    
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }

      //......
    }

FutureTask的異常處理

有了上文設(shè)置成功的邏輯解說,相信讀者對于setException的邏輯處理也就比較熟悉了,這里筆者就不再展開,讀者可自行查看代碼和注釋:

protected void setException(Throwable t) {
    //原子操作設(shè)置為完成中,保證可見性
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
         //結(jié)果設(shè)置為null
            outcome = t;
            //通過高性能有序?qū)憄utOrderedInt設(shè)置終態(tài)為錯誤態(tài)
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            //喚醒其他線程
            finishCompletion();
        }
    }

FutureTask如何取消

任務(wù)取消操作有兩種情況,如果我們傳參為true,則會通過原子操作將狀態(tài)設(shè)置為打斷中,再嘗試打斷正在執(zhí)行的線程,然后將狀態(tài)設(shè)置為已打斷這個終態(tài)INTERRUPTED,再喚醒其他線程。

若傳參設(shè)置為false,則直接原子操作設(shè)置為已取消,然后直接喚醒其他線程。

public boolean cancel(boolean mayInterruptIfRunning) {
//如果狀態(tài)不為new則進行狀態(tài)原子設(shè)置操作
        if (!(state == NEW &&
              UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            returnfalse;
        try {    
         //如果入?yún)閠rue則嘗試打斷線程
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                 //完成打斷設(shè)置狀態(tài)為INTERRUPTED
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
        //喚醒其他線程
            finishCompletion();
        }
        returntrue;
    }

FutureTask如何避免任務(wù)重復(fù)執(zhí)行

我們假設(shè)當(dāng)前任務(wù)為初始化NEW,這意味所有任務(wù)都可以嘗試進行上樂觀鎖獲取執(zhí)行勸,如下所示只有設(shè)置成功的線程才可以進入后續(xù)的執(zhí)行,而其他線程則直接返回:

public void run() {
  //只有狀態(tài)為NEW的情況下,當(dāng)前執(zhí)行的線程才會通過CAS獲取樂觀鎖,之后獲取樂觀鎖成功的才能執(zhí)行任務(wù),其他的線程會直接返回
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
  //......
  //執(zhí)行任務(wù)邏輯
}

JDK8版本FutureTask的變化

這一點,筆者參閱了1.6版本的FutureTask實現(xiàn),其內(nèi)部是通過AQS來維護需要執(zhí)行任務(wù)的線程及其狀態(tài),而JDK8版本則專門為FutureTask創(chuàng)建了一個state字段,多線程之間通過CAS操作進行維護。

我們先來說明一下早期版本的設(shè)計再說明原因,在較早的版本FutureTask內(nèi)部線程競爭關(guān)系和任務(wù)狀態(tài)都采用AQS進行維護,假設(shè)當(dāng)前任務(wù)被取消,則執(zhí)行這個操作線程會通過原子操作將AQS隊列的state字段更新為CANCELLED。

同理執(zhí)行任務(wù)時,也是先通過AQS的原子操作將狀態(tài)設(shè)置為RUNNING,執(zhí)行完成之后將操作結(jié)果原子修改為RAN,并將結(jié)果記錄到FutureTask的reuslt中:

對此我們給出老版本的FutureTask的run方法,邏輯如上文所說,筆者這里就不多做贅述:

public void run() {
        sync.innerRun();
    }

void innerRun() {
   //原子操作將狀態(tài)設(shè)置為RUNNING
            if (!compareAndSetState(0, RUNNING))
                return;
            try {
             //獲取當(dāng)前線程,檢查當(dāng)前狀態(tài)還是RUNNING則用innerSet當(dāng)前執(zhí)行執(zhí)行callable,然后將結(jié)果設(shè)置到result中,并將狀態(tài)修改為RAN
                runner = Thread.currentThread();
                if (getState() == RUNNING) // recheck after setting thread
                    innerSet(callable.call());
                else
                    releaseShared(0); // cancel
            } catch (Throwable ex) {
             //......
            }
        }

我們來看看cancel方法,可以看到只要不是RAN這種終態(tài),就可以嘗試打斷線程:

public boolean cancel(boolean mayInterruptIfRunning) {
        return sync.innerCancel(mayInterruptIfRunning);
    }

boolean innerCancel(boolean mayInterruptIfRunning) {
            for (; ; ) {
             //獲取狀
                int s = getState();
                //如果是RAN或者canceled則直接返回
                if (ranOrCancelled(s))
                    returnfalse;
                  //將狀態(tài)設(shè)置為CANCELLED
                if (compareAndSetState(s, CANCELLED))
                    break;
            }
            // 按照mayInterruptIfRunning的布爾值決定是否打斷線程
            if (mayInterruptIfRunning) {
                Thread r = runner;
                if (r != null)
                    r.interrupt();
            }
            releaseShared(0);
            done();
            returntrue;
        }

有意思的來了,我們試想這樣一個場景:

  • 假設(shè)我們執(zhí)行上述run方法的任務(wù)處于RUNNING狀態(tài),此時當(dāng)前線程1就可以調(diào)用interrupt打斷這個線程
  • 此時線程2看到當(dāng)前任務(wù)處于Running(源碼說明不是ran或者canceled即可打斷)直接將其打斷
  • 又假設(shè)我們這個線程跑去執(zhí)行別的task任務(wù)

所以線程1就可能在執(zhí)行別的任務(wù)期間被打斷,進而出現(xiàn)幻覺死:

于是就有了JDK7之后版本,F(xiàn)utureTask專門使用一個volatile的state維護任務(wù)的狀態(tài),并且在打斷前設(shè)置一個中態(tài)INTERRUPTING 即打斷中,執(zhí)行任務(wù)的線程1在運行完成將結(jié)果存入outcome之后,看到打斷中這個中態(tài)就會循環(huán)調(diào)用yield讓出執(zhí)行權(quán),直到執(zhí)行cancel操作的線程完成,由此保證了打斷操作永遠被限制在當(dāng)前FutureTask生命周期以內(nèi)。

對應(yīng)我們給出cancel操作的源碼,可以看到它是先設(shè)置為打斷中INTERRUPTING 然后在進行打斷再設(shè)置打斷完成INTERRUPTED的終態(tài):

public boolean cancel(boolean mayInterruptIfRunning) {
//原子操作設(shè)置狀態(tài)為打斷中
        if (!(state == NEW &&
              UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            returnfalse;
        try { 
         //嘗試打斷線程,完成后設(shè)置為INTERRUPTED
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        returntrue;
    }

假設(shè)上一步的線程的打斷操作還未完成,這里可以直接理解為執(zhí)行interrupt打斷之前的代碼段,而執(zhí)行我們的run的線程已經(jīng)運行完成并將結(jié)果設(shè)置到outcome時,如下所示會執(zhí)行finally 語句塊的handlePossibleCancellationInterrupt,它在看到打斷中的操作后會循環(huán)調(diào)用Thread.yield()讓當(dāng)前線程讓出CPU執(zhí)行權(quán),直到其他線程的cancel操作完成:

public void run() {
        //.....
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                 //執(zhí)行邏輯運算
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                   //......
                }
                //將結(jié)果設(shè)置到outcome中
                if (ran)
                    set(result);
            }
        } finally {
          //......
          //讀取到打斷中的狀態(tài),調(diào)用handlePossibleCancellationInterrupt讓線程yield出去,保證打斷操作限定在當(dāng)前線程內(nèi)
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }


private void handlePossibleCancellationInterrupt(int s) {
       //循環(huán)yield直到cacnel操作完成
        if (s == INTERRUPTING)
            while (state == INTERRUPTING)
                Thread.yield(); // wait out pending interrupt
    }

關(guān)于這個問題我們也可以參考源碼上的注釋:

/*
     * Revision notes: This differs from previous versions of this
     * class that relied on AbstractQueuedSynchronizer, mainly to
     * avoid surprising users about retaining interrupt status during
     * cancellation races. Sync control in the current design relies
     * on a "state" field updated via CAS to track completion, along
     * with a simple Treiber stack to hold waiting threads.
     *
     * Style note: As usual, we bypass overhead of using
     * AtomicXFieldUpdaters and instead directly use Unsafe intrinsics.
     */
責(zé)任編輯:趙寧寧 來源: 寫代碼的SharkChili
相關(guān)推薦

2020-11-03 09:10:18

JUC-Future

2023-10-11 18:35:20

Java編程語言

2011-10-10 09:35:54

云計算服務(wù)器

2009-07-10 16:14:29

MVC設(shè)計模式Swing

2022-11-15 09:57:51

Java接口

2022-06-02 11:12:10

CallableFuture

2010-06-11 14:55:20

2023-10-17 15:56:37

FutureTask線程

2020-12-28 07:33:21

SkipListJava跳表

2009-03-03 09:13:36

工作流BPM業(yè)務(wù)流程

2011-08-29 15:30:53

JavaMELua

2024-11-22 15:00:00

開源Redis鏈表

2015-11-03 09:28:52

Hybrid技術(shù)設(shè)計實現(xiàn)

2020-10-23 08:31:15

Nodejs-Ipc設(shè)計實現(xiàn)

2022-09-12 07:17:20

redis命令redissynce

2023-05-26 08:24:17

短信渠道模型

2023-08-10 10:13:35

轉(zhuǎn)轉(zhuǎn)短鏈平臺

2023-05-17 00:15:11

TCCXA模式

2022-09-14 09:37:22

數(shù)據(jù)系統(tǒng)
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 中文在线播放 | 97成人在线 | 在线黄 | 色视频免费| 韩日免费视频 | 久久高清亚洲 | gogo肉体亚洲高清在线视 | 日本在线视频一区二区 | 国产精品视频一区二区三区 | 国产午夜精品福利 | 免费看黄视频网站 | 国产在线视频一区二区 | 午夜精品久久久久99蜜 | 亚洲福利一区二区 | 欧美视频免费在线 | 欧美一区中文字幕 | 精品精品视频 | 久久99久久99久久 | 成人h片在线观看 | 亚洲视频1区 | 国产视频第一页 | 亚洲91视频 | 91精品久久 | 国产一区二区三区视频免费观看 | 久久九九免费 | 国产真实乱对白精彩久久小说 | 亚洲91精品 | 精品综合在线 | 精品国产欧美一区二区三区成人 | 91在线精品播放 | 操操操av| 一级毛片视频 | 视频在线亚洲 | 天啪| www.色午夜.com| 一级大片网站 | 日韩手机在线看片 | 精品久久久久久 | 99re热精品视频国产免费 | 亚洲一区二区三区免费视频 | 欧美日韩一区不卡 |