国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

追蹤解析 FutureTask 源碼

xcc3641 / 567人閱讀

摘要:零前期準備文章異常啰嗦且繞彎。版本版本簡介是中默認的實現(xiàn)類,常與結(jié)合進行多線程并發(fā)操作。所以方法的主體其實就是去喚醒被阻塞的線程。本文僅為個人的學習筆記,可能存在錯誤或者表述不清的地方,有緣補充

零 前期準備 0 FBI WARNING

文章異常啰嗦且繞彎。

1 版本

JDK 版本 : OpenJDK 11.0.1

IDE : idea 2018.3

2 ThreadLocal 簡介

FutureTask 是 jdk 中默認的 Future 實現(xiàn)類,常與 Callable 結(jié)合進行多線程并發(fā)操作。

3 Demo
import java.util.concurrent.*;

public class FutureTaskDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        
        //創(chuàng)建一個線程池
        ExecutorService pool = Executors.newFixedThreadPool(1);
        try{
            //創(chuàng)建一個要執(zhí)行的 Callable 對象
            //此處其實 Runnable 對象也可以,但是通常不會那樣做
            Callable task = () -> {
                //休眠三秒
                TimeUnit.SECONDS.sleep(3);
                //返回一個字符串
                return "hello";
            };

            //用 FutureTask 對象去包裝 Callable
            FutureTask futureTask = new FutureTask<>(task);

            //此處將 FutureTask 對象丟進線程池里
            pool.submit(futureTask);

            //注意,此處的 futureTask 本質(zhì)上是作為 Runnable 被丟進池子里的
            //所以也可以用線程池的 execute(...) 方法
            //pool.execute(futureTask)

            //還有一種更常見的執(zhí)行方式是直接使用 Thread
            //new Thread(futureTask).start();

            //獲取結(jié)果
            //注意,如果沒有獲取到的話此處會阻塞線程直到獲取到為止
            String result = futureTask.get();

            //還有一種限時策略的結(jié)果獲取
            //超時的情況下會拋出異常
            //String result = futureTask.get(1,TimeUnit.SECONDS);

            System.out.println(result);
        }finally {
            //關(guān)閉連接池
            pool.shutdown();
        }

    }
}
一 FutureTask 的創(chuàng)建

回到 Demo 中的創(chuàng)建代碼:

FutureTask futureTask = new FutureTask<>(task);

追蹤 FutureTask 的構(gòu)造器:

//FutureTask.class
public FutureTask(Callable callable) {
    //有效性判斷,不能為空
    if (callable == null)
        throw new NullPointerException();
    //記錄下 callable 對象
    this.callable = callable;
    //state 是一個 int 類型的對象,是一個
    //NEW = 0
    this.state = NEW;
}
二 run

FutureTask 本身是 Runnable 的實現(xiàn)類,其在被 ThreadPoolExecutor 或者 Thread 對象消費的時候也是被當做 Runnable 的實現(xiàn)類的。

所以其本身的核心邏輯就必然在 run() 方法中:

//FutureTask.class
public void run() {

    //先判斷狀態(tài),如果狀態(tài)不是 NEW 就會直接返回
    //RUNNER 是一個 VarHandler 類型的變量,指向了 FutureTask 中的 thread 變量,用于儲存當前的線程
    //但是如果 thread 已經(jīng)不為 null,此處也會直接返回
    //這兩種返回條件都意味著此 FutureTask 的 run() 方法已經(jīng)執(zhí)行過了
    if (state != NEW || !RUNNER.compareAndSet(this, null, Thread.currentThread()))
        return;

    try {
        //獲取 callable
        Callable c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                //執(zhí)行 callable 的業(yè)務(wù)邏輯
                result = c.call();
                //ran 為成功標識
                ran = true;
            } catch (Throwable ex) {
                //出錯的情況下
                result = null;
                ran = false;
                //不成功的情況下存入 exception
                setException(ex);
            }
            //如果成功的話會在此處進行操作
            if (ran)
                set(result);
        }
    } finally {
        //置空
        runner = null;
        int s = state;
        if (s >= INTERRUPTING)
            //如果此 FutreTask 的狀態(tài)是中斷狀態(tài),會在此處不斷調(diào)用 Thread.yield() 空轉(zhuǎn)
            handlePossibleCancellationInterrupt(s);
    }
}

此處有兩個關(guān)鍵方法,即為 setException(...) 和 set(...):

//FutureTask.class
protected void setException(Throwable t) {
    //用 CAS 操作比較并更新狀態(tài)值
    if (STATE.compareAndSet(this, NEW, COMPLETING)) {
        //outcome 是一個 Object 對象,用于存儲 callable 的返回值
        //此處由于報錯了,所以儲存的是錯誤對象
        outcome = t;
        //EXCEPTIONAL = 3
        STATE.setRelease(this, EXCEPTIONAL);
        //最后清理工作,主要用于喚醒等待線程和執(zhí)行 callable
        finishCompletion();
    }
}

//FutureTask.class
protected void set(V v) {
    //基本邏輯和 setException(...) 方法雷同,只是 STATE 和 outcome 的儲存值不同
    if (STATE.compareAndSet(this, NEW, COMPLETING)) {
        outcome = v;
        STATE.setRelease(this, NORMAL);
        finishCompletion();
    }
}

再來看 finishCompletion() 方法:

//FutureTask.class
private void finishCompletion() {
    //WaitNode 是 FutureTask 的靜態(tài)內(nèi)部類
    //其本質(zhì)上是單向鏈表的節(jié)點表示類,用于存放想要獲取 Callable 的返回值但是被阻塞的線程的線程對象
    for (WaitNode q; (q = waiters) != null;) {
        //此處使用 CAS 將 q 從 WAITERS 里去除
        if (WAITERS.weakCompareAndSet(this, q, null)) {
            for (;;) {
                Thread t = q.thread;
                if (t != null) {
                    //此處置空線程對象,幫助 GC
                    q.thread = null;
                    //喚醒線程
                    LockSupport.unpark(t);
                }
                //接著往下遍歷
                WaitNode next = q.next;
                if (next == null)
                    break;
                q.next = null; 
                q = next;
            }
            break;
        }
    }
    //此方法是空的
    done();
    //置空 callable
    callable = null;
}

之前提到過在 FutureTask 的 get(...) 方法中會阻塞線程,直到 Callable 執(zhí)行完畢并能夠獲取返回值的時候才會結(jié)束阻塞。

所以 finishCompletion() 方法的主體其實就是去喚醒被阻塞的線程。

三 get

回到 Demo 中的創(chuàng)建代碼:

String result = futureTask.get();

追蹤 get() 方法:

//step 1
//FutureTask.class
public V get() throws InterruptedException, ExecutionException {
    //此處先判斷狀態(tài)值,如果非 COMPLETING,即為還沒完成,就會調(diào)用 awaitDone(...) 方法阻塞線程
    int s = state;
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    //返回結(jié)果
    return report(s);
}

//step 2
//FutureTask.class
private V report(int s) throws ExecutionException {
    //獲取需要返回的對象
    Object x = outcome;
    //如果是正常結(jié)束的就直接返回對象即可
    if (s == NORMAL)
        return (V)x;
    //出錯的情況下,拋異常
    if (s >= CANCELLED)
        throw new CancellationException();
    throw new ExecutionException((Throwable)x);
}

再來看一下阻塞線程的 awaitDone(...) 方法:

private int awaitDone(boolean timed, long nanos) throws InterruptedException {
    
    //循環(huán)的次數(shù)
    long startTime = 0L;
    //節(jié)點對象
    WaitNode q = null;
    //鏈表隊列標識,代表該線程是否被加入鏈表中,初始為 false 代表未被加入
    boolean queued = false;
    for (;;) {
        int s = state;
        if (s > COMPLETING) { //如果 Callable 的執(zhí)行已經(jīng)完成
            if (q != null)
                q.thread = null;
            return s;
        }else if (s == COMPLETING) //Callable 的執(zhí)行剛剛完成,后續(xù)工作還沒做
            Thread.yield();
        else if (Thread.interrupted()) {
            //線程被中斷了,會拋出錯誤
            removeWaiter(q);
            throw new InterruptedException();
        } else if (q == null) { //進入此處的判斷證明 Callable 還未完成,所以會創(chuàng)建等待節(jié)點
            //此處的 timed 傳入為 false,不會在此返回
            if (timed && nanos <= 0L)
                return s;
            q = new WaitNode(); //新建節(jié)點
        }else if (!queued)
            //queued 初始為 false,進入此處的時候會將上一個判斷條件中新建的 q 加入到鏈表的首節(jié)點中
            //并且 queued 變成 true
            queued = WAITERS.weakCompareAndSet(this, q.next = waiters, q);
        else if (timed) {
            //如果此操作是限時的,那么這里需要判斷時間
            final long parkNanos;
            if (startTime == 0L) {
                startTime = System.nanoTime();
                if (startTime == 0L)
                    startTime = 1L;
                parkNanos = nanos;
            } else {
                long elapsed = System.nanoTime() - startTime;
                if (elapsed >= nanos) {
                    removeWaiter(q);
                    return state;
                }
                parkNanos = nanos - elapsed;
            }
            if (state < COMPLETING)
                //此處掛起線程,時間為 parkNanos
                //本例中傳入為 0L,所以是永久掛起
                LockSupport.parkNanos(this, parkNanos);
        }else
            //永久掛起線程
            LockSupport.park(this);
    }
}
四 一點嘮叨

FutureTask 和 ThreadLocal 一樣,都是 java.util.current 包中的小工具,封裝不復(fù)雜,理解即可。

本文僅為個人的學習筆記,可能存在錯誤或者表述不清的地方,有緣補充

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/73421.html

相關(guān)文章

  • FutureTask源碼解析(1)——預(yù)備知識

    摘要:在分析它的源碼之前我們需要先了解一些預(yù)備知識。因為接口沒有返回值所以為了與兼容我們額外傳入了一個參數(shù)使得返回的對象的方法直接執(zhí)行的方法然后返回傳入的參數(shù)。 前言 系列文章目錄 FutureTask 是一個同步工具類,它實現(xiàn)了Future語義,表示了一種抽象的可生成結(jié)果的計算。在包括線程池在內(nèi)的許多工具類中都會用到,弄懂它的實現(xiàn)將有利于我們更加深入地理解Java異步操作實現(xiàn)。 在分析...

    mmy123456 評論0 收藏0
  • 【Java并發(fā)】Runnable、Callable、Future、FutureTask

    摘要:聲明了幾種方法,其中有一個就是傳入聲明了對具體的或者任務(wù)執(zhí)行進行取消查詢結(jié)果獲取等方法。事實上,是接口的一個唯一實現(xiàn)類。使用示例第一種方式是使用繼承了的線程池中的方法,將直接提交創(chuàng)建。 創(chuàng)建線程的兩種方式 直接繼承 Thread 實現(xiàn) Runnable 接口 這兩種方式都有一個缺點:在執(zhí)行完成任務(wù)之后,無法直接獲取到最后的執(zhí)行結(jié)果。如果需要獲取執(zhí)行結(jié)果,就必須通過共享變量或線程通...

    zhaot 評論0 收藏0
  • FutureTask源碼解析(2)——深入理解FutureTask

    摘要:本文的源碼基于。人如其名,包含了和兩部分。而將一個任務(wù)的狀態(tài)設(shè)置成終止態(tài)只有三種方法我們將在下文的源碼解析中分析這三個方法。將棧中所有掛起的線程都喚醒后,下面就是執(zhí)行方法這個方法是一個空方 前言 系列文章目錄 有了上一篇對預(yù)備知識的了解之后,分析源碼就容易多了,本篇我們就直接來看看FutureTask的源碼。 本文的源碼基于JDK1.8。 Future和Task 在深入分析源碼之前,我...

    Harpsichord1207 評論0 收藏0
  • 系列文章目錄

    摘要:為了避免一篇文章的篇幅過長,于是一些比較大的主題就都分成幾篇來講了,這篇文章是筆者所有文章的目錄,將會持續(xù)更新,以給大家一個查看系列文章的入口。 前言 大家好,筆者是今年才開始寫博客的,寫作的初衷主要是想記錄和分享自己的學習經(jīng)歷。因為寫作的時候發(fā)現(xiàn),為了弄懂一個知識,不得不先去了解另外一些知識,這樣以來,為了說明一個問題,就要把一系列知識都了解一遍,寫出來的文章就特別長。 為了避免一篇...

    lijy91 評論0 收藏0
  • 系列文章目錄

    摘要:為了避免一篇文章的篇幅過長,于是一些比較大的主題就都分成幾篇來講了,這篇文章是筆者所有文章的目錄,將會持續(xù)更新,以給大家一個查看系列文章的入口。 前言 大家好,筆者是今年才開始寫博客的,寫作的初衷主要是想記錄和分享自己的學習經(jīng)歷。因為寫作的時候發(fā)現(xiàn),為了弄懂一個知識,不得不先去了解另外一些知識,這樣以來,為了說明一個問題,就要把一系列知識都了解一遍,寫出來的文章就特別長。 為了避免一篇...

    Yumenokanata 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<