ForkJoin全解2:forkjoin實際工作流程與實現

1、相關概念解釋

 

1.1 “內部”和外部

 

當一個操作是在非ForkjoinThread的線程中進行的,則稱該操作爲外部操作。比如我們前面執行pool.invoke,invoke內又執行externalPush。由於invoke是在非ForkjoinThread線程中進行的(這裏是在main線程中進行),所以是一個外部操作,調用的是externalPush。之後task的執行是通過ForkJoinThread來執行的,所以task中的fork就是內部操作,調用的是push,把任務提交到工作隊列。其實fork的實現是類似下面這樣的:

if(Thread.currentThread() instanceof ForkJoinThread){

         push(this)

}else{

         externaPush(this)

}

即fork會根據執行自身的線程是否是ForkJoinThread的實例來判斷是處於外部還是內部。那爲何要區分內外部?

任何線程都可以使用ForkJoin框架,但是對於非ForkJoinThread的線程,它到底是怎樣的,ForkJoin無法控制,也無法對其優化。因此區分出內外部,這樣方便ForkJoin框架對任務的執行進行控制和優化

1.2 Worker

從之前的敘述中,我們很可能會把線程跟worker等同起來,所以這裏要明確指出兩者是不同的。forkjoin框架中,worker更確切的指的是有owner的workQueue。forkjoin中,通過externalPush創建的workQueue是沒有owner的。

總之,不是所有workQueue都有owner,而有owner的workQueue就是worker。

2、整體工作流程圖

3、關鍵屬性解釋

3.1 pool的config屬性

config是int類型的,其高16位存儲mode,低16位存儲你指定的併發度。mode有2個值:LIFO_QUEUE = 0,即高16位爲0;以及FIFO_QUEUE  = 1 << 16,即高16位爲1。mode主要用於控制用什麼方法來獲取任務,如果是先進先出,則用poll方法獲取任務,如果是後進先出則用pop獲取任務。源碼示例:

(config & FIFO_QUEUE) == 0 ? pop() : poll();

當你new ForkJoinPool時,可以指定你要的併發度(parallelism),這個併發度將存儲在config的低16位中。

3.2 workQueues屬性

workQueues是pool的屬性,它是WorkQueue類型的數組。externalPush和externalSubmit所創建的workQueue沒有owner(即不是worker),且會被放到workQueues的偶數位置;而createWorker創建的workQueue(即worker)有owner,且會被放到workQueues的奇數位置。

3.3 workQueue的config屬性

這是WorkQueue的config,高16位跟pool的config值保持一致,而低16位則是workQueue在workQueues數組的位置。

從workQueues屬性的介紹中,我們知道,不是所有workQueue都有worker,沒有worker的workQueue稱爲公共隊列(shared queue),config的第32位就是用來判斷是否是公共隊列的。在externalSubmit創建工作隊列時,有:

q.config = k | SHARED_QUEUE;

其中q是新創建的workQueue,k就是q在workQueues數組中的位置,SHARED_QUEUE=1<<31,注意這裏config沒有保留mode的信息。

而在registerWorker中,則是這樣給workQueue的config賦值的:

w.config = i | mode;

w是新創建的workQueue,i是其在workQueues數組中的位置,沒有設置SHARED_QUEUE標記位

3.4 scanstate屬性

scanState是workQueue的屬性,是int類型的。scanState的低16位可以用來定位當前worker處於workQueues數組的哪個位置。每個worker在被創建時會在其構造函數中調用pool的registerWorker,而registerWorker會給scanState賦一個初始值,這個值是奇數,因爲worker是由createWorker創建,並會被放到WorkQueues的奇數位置,而createWorker創建worker時會調用registerWorker。

簡言之,worker的scanState初始值是奇數,非worker的scanstate初始值=INACTIVE=1<<31,小於0(非worker的workQueue在externalSubmit中創建)。

當每次調用signalWork(或tryRelease)喚醒worker時,worker的高16位就會加1

另外,scanState<0表示worker未激活,當worker調用runtask執行任務時,scanState會被置爲偶數,即設置scanState的最右邊一位爲0。

3.5 pool的ctl屬性

pool中ctl是long類型的,主要作用有2點:

  1. 存儲worker數目與目標併發度的關係
  2. 存儲休眠的worker

ctl是64位的,每16位就是一個子屬性,從高位到低位,4個子屬性如下:

  1. AC:最高的16位,值爲活躍的worker數減去目標並行度
  2. TC:AC之後的的6位,值爲總的worker數減去目標並行度。所以,如果TC<0,表示worker數小於目標並行度,此時就要添加worker了。換言之,最高位可用來表示worker數是否足夠,如果是1表示worker數不足夠,要添加worker。ForkJoinPool專門定義一個變量ADD_WORKER=1<<47來表示TC的最高位
  3. SS:等待隊列中,最頂部的線程的版本數
  4. ID:在無鎖棧中等待的線程的poolIndex

其中低32位又稱爲SP(其實是工作隊列的stackPred屬性的簡寫),高32位稱爲UC。那麼ctl是如何存儲休眠的worker以及如何喚醒worker的?

3.6 ctl,stackPred,與scanState實現worker休眠棧

 

worker休眠時,是這樣存儲的

int ctlHigh32=ctl >>>32;

int ctlLow32=(int)ctl;

ctl=ctlHigh32+worker.scanState

worker.preStack=ctlLow32

 

worker的喚醒類似這樣:

for(worker : pool.workQueues){

         if(worker.scanState==(int)ctl){

                  喚醒worker

                  worker.scanState的高16位加1

                  ctl的低32位=worker.preStack

                  退出循環

}

}

在worker休眠的4行僞碼中,讓ctl的低32位的值變爲worker.scanState,這樣下次就可以通過scanState喚醒該worker。喚醒該worker時,把該worker的preStack設置爲ctl低32位的值,這樣下下次喚醒的worker就是scanState等於該preStack的worker。

這裏通過preStack保存下一個worker,這個worker比當前worker更早地在等待,所以形成一個後進先出的棧。

3.7 ctl,stackPred,與scanState實現worker休眠棧

runState是int類型的值,控制整個pool的運行狀態和生命週期,有下面幾個值(可以好幾個值同時存在):

RSLOCK     = 1;

RSIGNAL    = 1 << 1;

STARTED    = 1 << 2;

STOP       = 1 << 29;

TERMINATED = 1 << 30;

SHUTDOWN   = 1 << 31;

如果runState值爲0,表示pool尚未初始化。

RSLOCK表示鎖定pool,當添加worker和pool終止時,就要使用RSLOCK鎖定整個pool。如果由於runState被鎖定,導致其他操作等待runState解鎖(通常用wait進行等待),當runState設置了RSIGNAL,表示runState解鎖,並通知(notifyAll)等待的操作。

剩下4個值都跟runState生命週期有關,都可以顧名思義:

當需要停止時,設置runState的STOP值,表示準備關閉,這樣其他操作看到這個標記位,就不會繼續操作,比如tryAddWorker看到STOP就不會再創建worker:

if(!stop){

         createWorker()

}

而tryTerminate對這些生命週期狀態的處理則是這樣的:

首先設置runState的SHUTDOWN,這樣isShutdown等方法可以使用這個狀態。然後判斷查看是否設置了stop,如果否,則會通過信息worker等方式加快任務的執行,讓任務儘快執行完畢,如果是則不會這樣。之後開始終止pool,最後設置runState爲TERMINATED。

要修改runState值,需要先調用lockRunstate,鎖定runstate。lockRunstate是線程安全的,如果鎖定失敗,線程會調用wait等待。如果鎖定成功,則使用unLockRunstate(oldRunstate,newRunstate),修改runstate值。unLockRunstate執行成功會調用notify喚醒那些在lockRunstate中等待的線程。

3.8 ctl,stackPred,與scanState實現worker休眠棧

當前top和base的初始值爲 INITIAL_QUEUE_CAPACITY >>>1= (1 << 13)>>>1 = 8192/2。然後push一個task之後,top+=1,也就是說,top對應的位置是沒有task的,最近push進來的task在top-1的位置。而base的位置則能對應到task,base對應最先放進隊列的task,top-1對應最後放進隊列的task。

3.9 workQueue的qlock

qlock值含義:1: locked, < 0: terminate; else 0

即當qlock值位0時,可以正常操作,值=1時,表示鎖定

4、相關算法解釋

4.1 求偶算法

int SQMASK=0x007e,則任何整數跟SQMASK位與後,得到的數就是偶數。

證明:

注意這裏化爲二進制是0111 1110,尤其注意最右邊第一位是0,任何數跟最右邊第一位是0的數位與後,得到的數就是偶數,因爲位與之後,第一位就是0,比如s=A&SQMASK,A可以是任意整數,然後把s按二進制進行多項式展開,則有s=2^n1+2^n2 ……+2^nn,這裏n≥1,所以s可以被2整除,即s是偶數。

所以一個數是奇數還是偶數,看其最右邊第一位即可。

4.2 workQueue的hint屬性與“奇數自加散列”算法

我們知道workQueue有externalPush創建的和createWorker創建的worker,兩種方式創建的workQueue,其放置到workQueues的位置是不同的,前者放到workQueue的偶數位置,而後者則放到奇數位置。不同workQueue找到自己在workQueues的位置的算法有點不同。

下面看一下forkjoin框架獲取workQueues中的偶數位置的workQueue的算法:

int r=ThreadLocalRandom.getProbe()

int m=workQueues.length-1,這裏workQueues.length是2的指數冪

int SQMASK=0x007e

workQueue=workQueues[m & r & SQMASK]

這樣就能獲取workQueues的偶數位置的workQueue。m保證m & r & SQMASK這整個運算結果不會超出workQueues的下標,SQMASK保證取到的是偶數位置的workQueue。這裏有一個有趣的現象,假設0到workQueues.length-1之間有n個偶數,m & r & SQMASK每次都能取到其中一個偶數,而且連續n次取到的偶數不會出現重複值,散列性非常好。而且是循環的,即1到n次取n個不同偶數,n+1到2n也是取n次不同偶數,此時n個偶數每個都被重新取一次。下面分析下r值有什麼祕密,爲何能保證這樣的散列性

ThreadLocalRandom內有一常量PROBE_INCREMENT = 0x9e3779b9,以及一個靜態的probeGenerator =new AtomicInteger() ,然後每個線程的probe= probeGenerator.addAndGet(PROBE_INCREMENT)所以第一個線程的probe值是0x9e3779b9,第二個線程的值就是0x9e3779b9+0x9e3779b9,第三個線程的值就是0x9e3779b9+0x9e3779b9+0x9e3779b9以此類推,整個值是線性的,可以用y=kx表示,其中k=0x9e3779b9,x表示第幾個線程。這樣每個線程的probe可以保證不一樣,而且具有很好的離散性。

實際上,可以不用0x9e3779b9這個值,用任意一個奇數都是可以的,比如1。如果用1的話,probe+=1,這樣每個線程的probe就都是不同的,而且具有很好的離散性。也就是說,假設有限制條件probe<n,超過n則產生溢出。則probe自加n次後纔會開始出現重複值,n次前probe每次自加的值都不同。實際上用任意一個奇數,都可以保證probe自加n次後纔會開始出現重複值,有興趣可看本文最後附錄部分。由於奇數的離散性,所以只要線程數小於m或者SQMASK兩者中的最小值,則每個線程都能唯一地佔據一個ws中的一個位置

  1. externalPush等外部操作創建的workQueue:使用上面介紹的方法來獲取偶數
  2. createWorker:與externalPush不同的是,pool內部有一個靜態常量SEED_INCREMENT=0x9e3779b9,以及一個普通屬性indexSeed=0

int r=indexSeed += SEED_INCREMENT,所以獲取的值跟externalPush是差不多的

無論哪種方式,最終都是workQueue.hint=r,即workQueue.hint的值就是用來定位workQueue所用的r。

5、任務提交

5.1 externalPush

根據執行任務提交的線程不同

示例中,forkJoinPool.invoke(task)是把任務放入工作隊列,並等待任務執行。源碼如下:

public <T> T invoke(ForkJoinTask<T> task) {
    if (task == null)
        throw new NullPointerException();
    externalPush(task);
    return task.join();
}

這裏externalPush負責任務提交,externalPush源碼如下:

final void externalPush(ForkJoinTask<?> task) {
    WorkQueue[] ws; WorkQueue q; int m;
    int r = ThreadLocalRandom.getProbe();

// runState有如下狀態值:

// RSLOCK     = 1;

// RSIGNAL    = 1 << 1;

// STARTED    = 1 << 2;

// STOP       = 1 << 29;

// TERMINATED = 1 << 30;

// SHUTDOWN   = 1 << 31;

//如果runstate爲0,表示無狀態,即當前沒有其他線程在執行其他操作,比如鎖定線程池,終止線程池等,小於0

//表示處於SHUTDOWN狀態

int rs = runState;

// workQueues!=null說明已經初始化過
    if ((ws = workQueues) != null && (m = (ws.length - 1)) >= 0 &&

    //q = ws[m & r & SQMASK]就是取ws中的偶數的隊列。另外m的值也要注意,m= ws.length - 1,

    //ws數組長度需要是2的指數冪,這樣m的值有效位就都是1,比如假設ws長度是8,則m=7,7的二進制有效位是

    //0111,注意後面三位全是1。因此保證ws長度是2的指數冪可以讓m的有效位都是1,從而位與的結果基本由r決

    //定,而m的作用就是用來限制位與後的結果不會超過m。

        (q = ws[m & r & SQMASK]) != null && r != 0

        //rs>0說明線程池在運行中,如果處於0,說明線程池還未初始化

        && rs > 0 &&

        //實現無鎖鎖定,跟鎖不同的是,執行CAS失敗的線程無法進入if內部,而是直接跳過if,執行if之外的代碼

       U.compareAndSwapInt(q, QLOCK, 0, 1)) {
        ForkJoinTask<?>[] a; int am, n, s;
        if ((a = q.array) != null && (am = a.length - 1) > (n = (s = q.top) - q.base)) {
            int j = ((am & s) << ASHIFT) + ABASE;
            U.putOrderedObject(a, j, task);
            U.putOrderedInt(q, QTOP, s + 1);

       //解鎖。因爲前面鎖定了代碼,所以這裏必須解鎖
            U.putIntVolatile(q, QLOCK, 0);

       //n <= 1說明此隊列任務之前被處理完畢,處理此隊列任務的線程可能處於等待狀態,故可能需要將這些線程喚

       //醒。如果這些線程已經終結,則要看當前活動線程數是否足夠,不足則需要添加線程
            if (n <= 1)
                signalWork(ws, q);
            return;
        }

    //解鎖
        U.compareAndSwapInt(q, QLOCK, 1, 0);
    }
    externalSubmit(task);
}

5.2 externalSubmit

externalSubmit是externalPush的完整版本,從externalPush源碼中可以看到,它把很多“疑難雜症”都交給externalSubmit處理,自己僅處理簡單的情況。前面externalPush中的第一個if有這樣一個條件:&&U.compareAndSwapInt(q, QLOCK, 0, 1),如果執行CAS失敗,難道就要把任務拋棄?顯然不是,所以看一下externalSubmit是怎麼處理這種情況的。

externalPush的註釋中,我們知道每個線程有自己的probe,通過probe,每個線程跟一個隊列綁定。externalSubmit的策略比較簡單,就是之前在externalPush中由於執行CAS失敗而無法push的任務,在externalSubmit再執行一次CAS,如果成功則把任務放入線程對應的隊列,如果失敗說明該隊列比較繁忙,所以externalSubmit就給該線程換一個probe,從而給該線程換一個隊列。顯然,這個過程是循環的,即每次執行CAS失敗externalSubmit就會給該線程換一個隊列,直到執行CAS成功爲止。

private void externalSubmit(ForkJoinTask<?> task) {

    int r;                                    // initialize caller's probe

    if ((r = ThreadLocalRandom.getProbe()) == 0) {

        ThreadLocalRandom.localInit();

        r = ThreadLocalRandom.getProbe();

    }

    for (;;) {

        WorkQueue[] ws; WorkQueue q; int rs, m, k;

        boolean move = false;

       // runstate小於0,此時runState對應的狀態是terminate

        if ((rs = runState) < 0) {

            tryTerminate(false, false);     // help terminate

            throw new RejectedExecutionException();

        }

       // 如果runState的STARTED對應的位是0,表示pool未啓動,所以要進行初始化,並啓動它

        else if ((rs & STARTED) == 0 ||    

                 ((ws = workQueues) == null || (m = ws.length - 1) < 0)) {

            int ns = 0;

            rs = lockRunState();

            try {

                if ((rs & STARTED) == 0) {

                    U.compareAndSwapObject(this, STEALCOUNTER, null,

                                           new AtomicLong());

                    // workQueues的 array的大小必須是2的指數冪

                    int p = config & SMASK; // ensure at least 2 slots

                    int n = (p > 1) ? p - 1 : 1;

                  // 這一堆讓人眼花繚亂的位移操作,是保證從n的最左邊的1開始,一直到最右邊的所有位都是1

                  //舉個例子,假設n的二進制爲0000 0000 1000 0000,則n |= n >>> 1後,n變爲

//0000 0000 1100 0000,注意原先的1後面一位變成了1,n |= n >>> 2後,變爲

//0000 0000 1111 0000,注意,原先2個1,然後這2個1後面多了2個1。以此類推,n |= n >>> 4

//則讓n的最右邊的1的後面4位變爲1,即n變爲了0000 0000 1111 1111。剩下的操作就沒任何效果了

//因爲後面沒有位數了。之後再加1,得到的數就是2的指數冪。

                    n |= n >>> 1; n |= n >>> 2;  n |= n >>> 4;

                    n |= n >>> 8; n |= n >>> 16; n = (n + 1) << 1;

                    workQueues = new WorkQueue[n];

                    ns = STARTED;

                }

            } finally {

                unlockRunState(rs, (rs & ~RSLOCK) | ns);

            }

        }

       //externalPush中,由於CAS失敗而提交失敗的任務,在這裏會被再次提交進去

        else if ((q = ws[k = r & m & SQMASK]) != null) {

            if (q.qlock == 0 && U.compareAndSwapInt(q, QLOCK, 0, 1)) {

                ForkJoinTask<?>[] a = q.array;

                int s = q.top;

                boolean submitted = false; // initial submission or resizing

                try {                      // locked version of push

                    if ((a != null && a.length > s + 1 - q.base) ||

                        (a = q.growArray()) != null) {

                        int j = (((a.length - 1) & s) << ASHIFT) + ABASE;

                        U.putOrderedObject(a, j, task);

                        U.putOrderedInt(q, QTOP, s + 1);

                        submitted = true;

                    }

                } finally {

                    U.compareAndSwapInt(q, QLOCK, 1, 0);

                }

                if (submitted) {

                    signalWork(ws, q);

                    return;

                }

            }

            move = true;                   // move on failure

        }

       //創建新隊列

        else if (((rs = runState) & RSLOCK) == 0) {

            q = new WorkQueue(this, null);

            q.hint = r;

            q.config = k | SHARED_QUEUE;

            q.scanState = INACTIVE;

            rs = lockRunState();           // publish index

            if (rs > 0 &&  (ws = workQueues) != null &&

                k < ws.length && ws[k] == null)

                ws[k] = q;                 // else terminated

            unlockRunState(rs, rs & ~RSLOCK);

        }

        else

            move = true;                   // move if busy

        if (move)

            r = ThreadLocalRandom.advanceProbe(r);

    }

}

5.3 任務提交中用到的同步機制

externalSubmit和externalPush都使用一個CAS操作來保證同步:

U.compareAndSwapInt(q, QLOCK, 0, 1)

其實forkjoinPool內workQueues數組已經設置的比較大了,爲2^16=65536,外部線程對任務的提交只用到其中的偶數部分,但也有32768。通常線程數上百已經算比較多的了,但是相對於32786而言,還是比較小的,所以會發生衝突的機率就很小。這也是爲何使用CAS來簡單地保證同步的原因。

6、創建和喚醒worker

6.1 喚醒和創建worker:signalWork

final void signalWork(WorkQueue[] ws, WorkQueue q) {
    long c; int sp, i; WorkQueue v; Thread p;
	// ctl<0表示活躍worker少於目標並行度
    while ((c = ctl) < 0L) {  
//爲0表示沒有正在等待的worker,即沒有空閒worker
        if ((sp = (int)c) == 0) {      
   // 表示總線程數少於目標併發度,需要添加worker         
            if ((c & ADD_WORKER) != 0L)            
                tryAddWorker(c);
            break;
        }
         // 線程池未開始運行,即未初始化或者已經終止
if (ws == null)                           
            break;
        if (ws.length <= (i = sp & SMASK))         // terminated
            break;
        if ((v = ws[i]) == null)                   // terminating
            break;
        int vs = (sp + SS_SEQ) & ~INACTIVE;        // next scanState
        int d = sp - v.scanState;                  // screen CAS
        long nc = (UC_MASK & (c + AC_UNIT)) | (SP_MASK & v.stackPred);
        if (d == 0 && U.compareAndSwapLong(this, CTL, c, nc)) {
            v.scanState = vs;                      // activate v
            if ((p = v.parker) != null)
                U.unpark(p);
            break;
        }
        if (q != null && q.base == q.top)          // no more work
            break;
    }
}

6.2 添加worker:tryAddWorker與createWorker

tryAddWorker通過createWorker創建worker

private void tryAddWorker(long c) {
    boolean add = false;
do {
		//AC和TC加1
        long nc = ((AC_MASK & (c + AC_UNIT)) |
                   (TC_MASK & (c + TC_UNIT)));
        if (ctl == c) {
            int rs, stop;                 // check if terminating
            if ((stop = (rs = lockRunState()) & STOP) == 0)
                add = U.compareAndSwapLong(this, CTL, c, nc);
            unlockRunState(rs, rs & ~RSLOCK);
            if (stop != 0)
                break;
            if (add) {
                createWorker();
                break;
            }
        }
    } while (((c = ctl) & ADD_WORKER) != 0L && (int)c == 0);
}

private boolean createWorker() {
    ForkJoinWorkerThreadFactory fac = factory;
    Throwable ex = null;
    ForkJoinWorkerThread wt = null;
    try {
        if (fac != null && (wt = fac.newThread(this)) != null) {
		//這裏啓動了線程
            wt.start();
            return true;
        }
    } catch (Throwable rex) {
        ex = rex;
}
//出現異常,取消註冊
    deregisterWorker(wt, ex);
    return false;
}

6.3 registerWorker

registerWorker在ForkJoinThread的構造函數中被調用,這是ForkJoinThread向pool註冊自身的一個方法。

final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
UncaughtExceptionHandler handler;
//配置worker爲守護worker,所以worker會隨main的結束而自動結束
    wt.setDaemon(true);                           // configure thread
    if ((handler = ueh) != null)
        wt.setUncaughtExceptionHandler(handler);
    WorkQueue w = new WorkQueue(this, wt);
    int i = 0;                                    // assign a pool index
    int mode = config & MODE_MASK;
    int rs = lockRunState();
    try {
        WorkQueue[] ws; int n;                    // skip if no array
        if ((ws = workQueues) != null && (n = ws.length) > 0) {
            int s = indexSeed += SEED_INCREMENT;  // unlikely to collide
            int m = n - 1;
            i = ((s << 1) | 1) & m;               // odd-numbered indices
            if (ws[i] != null) {                  // collision
                int probes = 0;                   // step by approx half n
                int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2;
                while (ws[i = (i + step) & m] != null) {
                    if (++probes >= n) {
                        workQueues = ws = Arrays.copyOf(ws, n <<= 1);
                        m = n - 1;
                        probes = 0;
                    }
                }
            }
            w.hint = s;                           // use as random seed
            w.config = i | mode;
            w.scanState = i;                      // publication fence
            ws[i] = w;
        }
    } finally {
        unlockRunState(rs, rs & ~RSLOCK);
    }
    wt.setName(workerNamePrefix.concat(Integer.toString(i >>> 1)));
    return w;
}

6.3 ForkJoinThread的run

public void run() {
if (workQueue.array == null) { // only run once
        Throwable exception = null;
        try {
            onStart();
	//從這裏開始,worker開始執行任務
            pool.runWorker(workQueue);
        } catch (Throwable ex) {
            exception = ex;
        } finally {
            try {
                onTermination(exception);
            } catch (Throwable ex) {
                if (exception == null)
                    exception = ex;
            } finally {
                pool.deregisterWorker(this, exception);
            }
        }
    }
}

7、任務執行

線程啓動後會執行pool的runWorker:

final void runWorker(WorkQueue w) {
w.growArray();                   // allocate queue
    int seed = w.hint;               // initially holds randomization hint
    int r = (seed == 0) ? 1 : seed;  // avoid 0 for xorShift
    for (ForkJoinTask<?> t;;) {
        if ((t = scan(w, r)) != null)
            w.runTask(t);
        else if (!awaitWork(w, r))
            break;
        r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift
    }
}
final void runTask(ForkJoinTask<?> task) {
if (task != null) {
        scanState &= ~SCANNING; // mark as busy
        (currentSteal = task).doExec();
        U.putOrderedObject(this, QCURRENTSTEAL, null); // release for GC
        execLocalTasks();
        ForkJoinWorkerThread thread = owner;
 //這裏因爲runWorker竊取了一個任務,所以需要++nsteals
 if (++nsteals < 0)      // collect on overflow
            transferStealCount(pool);
        scanState |= SCANNING;
        if (thread != null)
            thread.afterTopLevelExec();
    }
}

7.1 任務掃描(任務竊取)

scan的作用是掃描workQueues,竊取一個task,因爲worker剛剛創建,所以worker的工作隊列是空的,必須從外面掃一個task來執行。如果掃描來的task在被執行時調用了task.fork,生成新的task,則新的task就會被push到這個worker的工作隊列。之後worker會執行自身工作隊列中的task。當自身工作隊列執行完畢,進入下一次循環,再次掃描task。如果掃描不到task,說明worker,多餘了,即現有的worker已經足夠執行workQueues中的任務了,此時worker應當休眠,在產生新task時,可能會被喚醒。如果workQueues已經沒有多餘任務可以執行,說明pool要終止了,則應當終止worker。worker應當休眠還是終止,這個邏輯在awaitWork實現,可查看後續章節

// w是一個worker,r是一個隨機種子,用於獲取workQueues中的一個workQueue
private ForkJoinTask<?> scan(WorkQueue w, int r) {
    WorkQueue[] ws; int m;
    if ((ws = workQueues) != null && (m = ws.length - 1) > 0 && w != null) {
        int ss = w.scanState;                     // initially non-negative
        for (int origin = r & m, k = origin, oldSum = 0, checkSum = 0;;) {
            WorkQueue q; ForkJoinTask<?>[] a; ForkJoinTask<?> t;
            int b, n; long c;
		//如果獲取到的隊列不爲空,開始掃描task
            if ((q = ws[k]) != null) {
			//如果q有task
                if ((n = (b = q.base) - q.top) < 0 &&(a = q.array) != null) {      // non-empty
                    long i = (((a.length - 1) & b) << ASHIFT) + ABASE;
			   //如果能從q獲取到task。
                    if ((t = ((ForkJoinTask<?>)U.getObjectVolatile(a, i))) != null && 
//這裏q.base == b是保證base不變,如果base變了,說明有另一個線程獲取到這個base
//對應的任務且很可能被執行了,所以如果base變了,獲取到的task就無效了
q.base == b) {
                        //如果w是激活的
                        if (ss >= 0) {
					//如果CAS失敗,說明有其他線程在操作a,此時應換一個隊列,即更改r值並執行continue
					//進行新一輪的循環
                            if (U.compareAndSwapObject(a, i, t, null)) {
                                q.base = b + 1;
                                //如果q剩餘的任務數大於1,則嘗試喚醒或創建worker,加速任務的處理
                                if (n < -1)       // signal others
                                    signalWork(ws, q);
                                return t;
                            }
                        }
				// 如果w未激活,但是這時候又能獲得task,說明任務可能比較多,現有的活動的worker可能
				//處理不過來,所以嘗試激活worker,增加活動的worker
                        else if (oldSum == 0 &&  
                                 w.scanState < 0)
					//嘗試喚醒休眠的worker或創建worker
                            tryRelease(c = ctl, ws[m & (int)c], AC_UNIT);
                    }
				//如果獲取不到task,且w未激活
                    if (ss < 0)                   // refresh
				//把w.scanState值重新賦予ss,因爲這期間可能有其他線程激活了w。
                        ss = w.scanState;
                    r ^= r << 1; r ^= r >>> 3; r ^= r << 10;
		//origin,scan是從origin對應的位置掃描隊列的,即q=workQueues[origin]但是如果
		//隊列q競爭比較大,即有多個worker在操作它,則需要換一個隊列重新掃描,即執行下面的
		// r ^= r << 1; r ^= r >>> 3; r ^= r << 10;和 origin = k = r & m,continue重新掃描。
		//所以origin對應的隊列是非空,且有task,而且競爭度低的隊列
                    origin = k = r & m;           // move and rescan
                    oldSum = checkSum = 0;
                    continue;
                }
                checkSum += b;
            }
		// 判斷是否要講worker設置爲未激活。然後分析下怎樣的worker應該設置爲失活。首先
		//(k = (k + 1) & m) == origin,要滿足這個條件必須保證origin競爭度低,且worker是激活狀態
		//由於k每次加1,所以當k==origin,此時說明workQueues中的元素被遍歷了一遍。但仍然獲取不到task
            if ((k = (k + 1) & m) == origin) {    // continue until stable
			//如果worker是激活狀態則直接進入if內部把worker設置爲失活。注意這裏的失活是僞失活,因爲
			//僅僅是把當前worker的scanstate設置到ctl,而沒有真正讓線程休眠。線程設置爲失活後,還會繼續
			//掃描任務,因爲此時還未退出循環。當代碼再次來到這裏時,此時worker是僞失活狀態,且
			//workQueues又被遍歷了一遍。這時候判斷ss == (ss = w.scanState))就是判斷worker有沒有
			//被其他線程或者外部線程調用signalWork或者tryRelease進行喚醒。如果滿足則判斷
			// oldSum == (oldSum = checkSum),這個條件要滿足,當前取到的q必須爲空,如果q不爲空,要麼
			// checkSum被更改,導致不相等,要麼就進入重新掃描,代碼到不了這裏。由於僞失活後,循環沒有
			//結束,仍然在繼續掃描。如果僞失活後,掃到的隊列一直非空,說明任務比較多,現有的活動的worker
			//可能處理不過來,所以僞失活worker會繼續掃描。如果掃到爲空的隊列,則退出循環,然後交由
			//awaitWork來真正讓worker休眠或者終止worker 
                if ((ss >= 0 || (ss == (ss = w.scanState))) &&
                    oldSum == (oldSum = checkSum)) {
                    if (ss < 0 || w.qlock < 0)    // already inactive
                        break;
                    int ns = ss | INACTIVE;       // try to inactivate
                    long nc = ((SP_MASK & ns) |
                               (UC_MASK & ((c = ctl) - AC_UNIT)));
                    w.stackPred = (int)c;         // hold prev stack top
                    U.putInt(w, QSCANSTATE, ns);
                    if (U.compareAndSwapLong(this, CTL, c, nc))
                        ss = ns;
                    else
                        w.scanState = ss;         // back out
                }
			//worker僞失活後,重新掃描
                checkSum = 0;
            }
        }
    }
    return null;
}

7.2 worker休眠與終結

如果返回false表示worker需要終結。

private boolean awaitWork(WorkQueue w, int r) {
     //qlock值的含義:1: locked, < 0: terminate; else 0,所以這裏小於0表示終止
    if (w == null || w.qlock < 0)                 // w is terminating
        return false; 
for (int pred = w.stackPred, spins = SPINS, ss;;) {
	  //表示worker被重新激活了,跳出循環返回true
        if ((ss = w.scanState) >= 0)
            break;
	//空轉,當前SPINS值爲0,表示不空轉。空轉表示執行的代碼啥也不敢,就是拖拖時間,看空轉期間相關的狀態是否
	//被其他線程更改
        else if (spins > 0) {
            r ^= r << 6; r ^= r >>> 21; r ^= r << 7;
            if (r >= 0 && --spins == 0) {         // randomize spins
                WorkQueue v; WorkQueue[] ws; int s, j; AtomicLong sc;
                if (pred != 0 && (ws = workQueues) != null &&
                    (j = pred & SMASK) < ws.length &&
                    (v = ws[j]) != null &&        // see if pred parking
                    (v.parker == null || v.scanState >= 0))
                    spins = SPINS;                // continue spinning
            }
        }
        else if (w.qlock < 0)                     // recheck after spins
            return false;
        else if (!Thread.interrupted()) {
            long c, prevctl, parkTime, deadline;
            int ac = (int)((c = ctl) >> AC_SHIFT) + (config & SMASK);
		//這裏由於採用了非阻塞策略,所以tryTerminate中調用Thread.interrupt並不會讓當前線程
		//拋出中斷異常而終止,所以需要做一下判斷
            if ((ac <= 0 && tryTerminate(false, false)) ||
                (runState & STOP) != 0)           // pool terminating
                return false;
		//當前沒有活躍worker,且當前worker處於worker等待棧的頂部
            if (ac <= 0 && ss == (int)c) {        // is last waiter
                prevctl = (UC_MASK & (c + AC_UNIT)) | (SP_MASK & pred);
                int t = (short)(c >>> TC_SHIFT);  // shrink excess spares
			//因爲當前已經沒有活躍線程了,這意味着任務已經執行完畢,且沒有調用pool的shutdown關閉
		//pool,所以留下一部分線程,這樣當有新任務來時,可以複用這些線程。但是留下的線程也不能太多
		//畢竟目前已經沒有要處理的任務了。t>2表示已經預留了足夠多的線程了,而使用CAS則是因爲CTL有可能
		//被其他線程更改,爲何還會有其他線程更改呢?有這幾種情況,1是因爲其他線程在執行tryTerminate把
		//自己設置爲未激活狀態,從而讓自己處於worker等待棧的頂部,而當前worker則不再處於worker等待棧
		//的頂部,棧中元素得一個一個出纔行,不能出棧任意位置的元素,否則會有線程安全問題。另外則是可能
		//“來活了”,即有新任務來了,所以有線程被激活或有新線程被創建。所以必須滿足下面if的兩個條件才能
		//終止當前worker
                if (t > 2 && U.compareAndSwapLong(this, CTL, c, prevctl))
                    return false;                 // else use timed wait
                parkTime = IDLE_TIMEOUT * ((t >= 0) ? 1 : 1 - t);
                deadline = System.nanoTime() + parkTime - TIMEOUT_SLOP;
            }
            else
                prevctl = parkTime = deadline = 0L;
            Thread wt = Thread.currentThread();
            U.putObject(wt, PARKBLOCKER, this);   // emulate LockSupport
            w.parker = wt;
            if (w.scanState < 0 && ctl == c)      // recheck before park
                U.park(false, parkTime);
		//如果線程等待了一段時間發現還沒有任務要執行,則終止自己,否則線程自動醒來執行任務
            U.putOrderedObject(w, QPARKER, null);
            U.putObject(wt, PARKBLOCKER, null);
            if (w.scanState >= 0)
                break;
            if (parkTime != 0L && ctl == c &&
                deadline - System.nanoTime() <= 0L &&
                U.compareAndSwapLong(this, CTL, c, prevctl))
                return false;                     // shrink pool
        }
    }
    return true;
}

8、任務分割與等待:fork和join

fork和join是ForkJoinTask的方法,也是整個框架的設計靈魂:fork把任務切分爲小任務,join則等待任務結果。

8.1 fork

fork的實現異常簡單:

public final ForkJoinTask<V> fork() {
Thread t;
    if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
	//放到當前worker的workQueue
        ((ForkJoinWorkerThread)t).workQueue.push(this);
    else
	//common是一個靜態的,ForkJoinPool實例,不可關閉,所以即使你不new一個ForkJoinPool
	//直接調用fork,此時任務就被提交到common了
        ForkJoinPool.common.externalPush(this);
    return this;
}

8.2 join

public final V join() {
int s;
//doJoin是一個等待任務執行完畢的方法,當任務執行完畢就會返回
//這裏雖然等待,但是卻並不一定阻塞
    if ((s = doJoin() & DONE_MASK) != NORMAL)
        reportException(s);
    return getRawResult();
}
private int doJoin() {
    int s; Thread t; ForkJoinWorkerThread wt; ForkJoinPool.WorkQueue w;


//status表示任務執行狀態,有如下值:
// static final int NORMAL      = 0xf0000000;  // 必須爲負數
// static final int CANCELLED   = 0xc0000000;  // 必須 < NORMAL
// static final int EXCEPTIONAL = 0x80000000;  // 必須 < CANCELLED
// static final int SIGNAL      = 0x00010000;  //必須 >= 1 << 16
//所以status<0表示任務已經完成,或取消,或拋異常,無論怎樣,就是任務結束了
//下面這段代碼的意思是,任務是否結束,是則返回,否則看任務執行線程是否是ForkJoinWorkerThread實例
//否則externalAwaitDone()等待,是則嘗試讓任務出隊並執行,如果不能出隊或者執行無法完成,則使用
// wt.pool.awaitJoin(w, this, 0L)等待。externalAwaitDone()基本就是wait和notify,而awaitJoin
//就複雜點,需要幫助竊取當前任務的worker(稱爲stealer)儘快執行任務,即會竊取stealer的任務來執行
return (s = status) < 0 ? s :
        ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
        (w = (wt = (ForkJoinWorkerThread)t).workQueue).
        tryUnpush(this) && (s = doExec()) < 0 ? s : //嘗試從自身隊列中獲取當前任務來執行
        wt.pool.awaitJoin(w, this, 0L) :
        externalAwaitDone();
}


private int externalAwaitDone() {
    int s = ((this instanceof CountedCompleter) ? // try helping
             ForkJoinPool.common.externalHelpComplete(
                 (CountedCompleter<?>)this, 0) :
             ForkJoinPool.common.tryExternalUnpush(this) ? doExec() : 0);
    if (s >= 0 && (s = status) >= 0) {
        boolean interrupted = false;
        do {
            if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
                synchronized (this) {
                    if (status >= 0) {
                        try {
                            wait(0L);
                        } catch (InterruptedException ie) {
                            interrupted = true;
                        }
                    }
                    else
                        notifyAll();
                }
            }
        } while ((s = status) >= 0);
        if (interrupted)
            Thread.currentThread().interrupt();
    }
    return s;
}

final int awaitJoin(WorkQueue w, ForkJoinTask<?> task, long deadline) {
    int s = 0;
    if (task != null && w != null) {
	// currentJoin這個屬性基本僅在這個方法內使用,這裏先把currentJoin保存在prevJoin
	//而把當前任務引用賦予currentJoin,所以這裏可以看出,任務之間必須是有向無環圖,比如任務A和B
	//不能A等待B完成,然後B又等待A完成,這樣會造成死鎖。不過真遇到這種情況,也有解決方法,就是
	//設置等待超時時間,所以下面的wait即internalWait的等待是有超時時間的
        ForkJoinTask<?> prevJoin = w.currentJoin;
        U.putOrderedObject(w, QCURRENTJOIN, task);
        CountedCompleter<?> cc = (task instanceof CountedCompleter) ?
            (CountedCompleter<?>)task : null;
        for (;;) {
	//如果task完成
            if ((s = task.status) < 0)
                break;
            if (cc != null)
                helpComplete(w, cc, 0);
	//如果當前工作隊列沒有任務,且task未完成,則task被人竊取了
            else if (w.base == w.top || w.tryRemoveAndExec(task))
		//幫助stealer執行任務,畢竟當前自身隊列的任務已經執行完了,所以幫助stealer執行任務, 
		//以儘快能夠返回
                helpStealer(w, task);
            if ((s = task.status) < 0)
                break;
            long ms, ns;
            if (deadline == 0L)
                ms = 0L;
            else if ((ns = deadline - System.nanoTime()) <= 0L)
                break;
            else if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) <= 0L)
                ms = 1L;
	//嘗試增加一個線程作爲補償,因爲當前線程準備進入等待,能到達這裏,表示
	//任務未完成,且工作隊列還有任務
            if (tryCompensate(w)) {
                task.internalWait(ms);
                U.getAndAddLong(this, CTL, AC_UNIT);
            }
        }
        U.putOrderedObject(w, QCURRENTJOIN, prevJoin);
    }
    return s;
}


/**
 * 從隊列中獲取指定task來執行,或其他已經取消的task。此方法僅被awaitJoin使用
 * @return true 如果隊列爲空,且task不知是否完成時
 */
final boolean tryRemoveAndExec(ForkJoinTask<?> task) {
    ForkJoinTask<?>[] a; int m, s, b, n;
    if ((a = array) != null && (m = a.length - 1) >= 0 &&
        task != null) {
        while ((n = (s = top) - (b = base)) > 0) {
            for (ForkJoinTask<?> t;;) {      // traverse from s to b
		//從top的前一個位置,即第一個元素開始遍歷
                long j = ((--s & m) << ASHIFT) + ABASE;
                if ((t = (ForkJoinTask<?>)U.getObject(a, j)) == null)
		//如果獲取到的task爲空,s+1==top爲true,表示當前隊列爲空,否則非空
		//如果隊列非空,說明隊列內的元素都遍歷過了
                    return s + 1 == top;     // shorter than expected
                else if (t == task) {
                    boolean removed = false;
                    if (s + 1 == top) {      // pop
                        if (U.compareAndSwapObject(a, j, task, null)) {
                            U.putOrderedInt(this, QTOP, s);
                            removed = true;
                        }
                    }
		//如果到這裏,說明獲取的t是在base和top-2之間的。base == b表示任務沒有被偷,
		//因爲如果此時有任務被偷,可能被偷的任務就是t,所以加一層判斷
                    else if (base == b)      // replace with proxy
                        removed = U.compareAndSwapObject(
                            a, j, task, new EmptyTask());
                    if (removed)
                        task.doExec();
                    break;
                }
                else if (t.status < 0 && s + 1 == top) {
                    if (U.compareAndSwapObject(a, j, t, null))
                        U.putOrderedInt(this, QTOP, s);
                    break;                  // was cancelled
                }
                if (--n == 0)
                    return false;
            }
            if (task.status < 0)
                return false;
        }
    }
    return true;
}
/**
* 主要幫助stealer執行任務。當執行此方法時當前worker處於join等待子任務執行完畢,但子任務被其他worker 
* 竊取所以一定有一個stealer,可以通過遍歷所有worker,看它們的currentSteal是否跟等待的task匹配,如果
* 匹配的話,該worker就是stealer。然後竊取該stealer的任務來執行。藉此加速自身的子任務執行,因爲當
* stealer任務執行完畢,意味着worker當前執行的任務的join將有返回結果。在執行竊取的任務期間,也可能會
* 產生新的子任務以及發生對子任務的join,這個過程類似這樣:
* 主任務join ->執行竊取的任務,導致生成子任務 -> 子任務join -> 子任務執行後返回
* 返回過程則反過來
* 子任務執行後返回-> 子任務join結束並返回->竊取的任務join結束,並返回->主任務join結束
* 由於每個worker自身就能處理fork產生的子任務,且任務沒有循環依賴,所以整個過程不會出現死鎖的情況
* 另外,worker也可能找不到stealer。如果找不到stealer,則表示當前task已經被執行,如果task的狀態是
* 還未執行完畢,則執行這個task的子任務的stealer也肯定處於join,且在竊取任務來執行。而worker則因爲
* 找不到stealer,要麼等待,要麼空轉.整個實現思想是:執行任務,如果有子任務,則join時處理子任務,因爲只有
* 子任務處理完,任務才能完成,所以要先幫助子任務執行。如果子任務被竊取,則找到stealer,幫助stealer執行
* 從而幫助子任務的執行。但是如果任務被竊取,又找不到stealer,說明子任務正在其他worker上執行,所以當前
* worker就只能等待或空轉了
*/
private void helpStealer(WorkQueue w, ForkJoinTask<?> task) {
WorkQueue[] ws = workQueues;
    int oldSum = 0, checkSum, m;
    if (ws != null && (m = ws.length - 1) >= 0 && w != null &&
        task != null) {
        do {                                       // restart point
            checkSum = 0;                          // for stability check
            ForkJoinTask<?> subtask;
           // j是等待task完成,要進行join的worker,v是task的stealer
	 WorkQueue j = w, v;                    
            descent: for (subtask = task; subtask.status >= 0; ) {
		//這個for是查找竊取subtask的stealer,但無論是否能找到這個worker,最終都會返回v
                for (int h = j.hint | 1, k = 0, i; ; k += 2) {
                    if (k > m)                     // can't find stealer
                        break descent;
                    if ((v = ws[i = (h + k) & m]) != null) {
                        if (v.currentSteal == subtask) {
                            j.hint = i;
                            break;
                        }
                        checkSum += v.base;
                    }
                }
	//第一輪循環如果能找到stealer,且當前處於join,則竊取stealer的task來執行。
	//第二輪循環,如果stealer處於join,則stealer將竊取stealer的stealer的task來執行
	//第三輪就是處理stealer的stealer,以此類推。
                for (;;) {                         // help v or descend
                    ForkJoinTask<?>[] a; int b;
                    checkSum += (b = v.base);
                    ForkJoinTask<?> next = v.currentJoin;
		//subtask執行完畢
                    if (subtask.status < 0 || 
		//第一輪循環的時候這不可能發生,因爲j就是傳進來的worker,而subtask就是task
		//但是第二輪的時候,j是stealer,而subtask就是stealer之前的currentjoin,這表示
		//stealer把之前currentJoin的任務執行完了,而這個currentJoin的任務可能是當前task
		//的子任務所以退出循環,看當前task是否已經執行完。而且這個for本來就是幫助stealer
		//在處於join時,竊取別人任務來執行的,現在currentJoin變了,就沒有繼續的必要
		j.currentJoin != subtask ||
		//找不到stealer。找不到的原因是,上一個for中沒找到,或者上一個for中找到了
		//但由於stealer在執行竊取的任務,且這時候也在執行此方法,因此改了currentSteal
		//具體看下面執行竊取任務的部分的代碼。無論什麼原因,最終都等價於找不到stealer
                        v.currentSteal != subtask) // stale
                        break descent;
                    if (b - v.top >= 0 || (a = v.array) == null) {
                        if ((subtask = next) == null)
                            break descent;
                        j = v;
                        break;
                    }
                    int i = (((a.length - 1) & b) << ASHIFT) + ABASE;
                    ForkJoinTask<?> t = ((ForkJoinTask<?>)
                                         U.getObjectVolatile(a, i));
		//準備執行竊取的任務
                    if (v.base == b) {
                        if (t == null)             // stale
                            break descent;
                        if (U.compareAndSwapObject(a, i, t, null)) {
                            v.base = b + 1;
                            ForkJoinTask<?> ps = w.currentSteal;
                            int top = w.top;
                            do {
                                U.putOrderedObject(w, QCURRENTSTEAL, t);
                                t.doExec();        // clear local tasks too
                            } while (task.status >= 0 &&
                                     w.top != top &&
                                     (t = w.pop()) != null);
                            U.putOrderedObject(w, QCURRENTSTEAL, ps);
                            if (w.base != w.top)
                                return;            // can't further help
                        }
                    }
                }
            }
        } while (task.status >= 0 && oldSum != (oldSum = checkSum));
    }
}

9、pool終止

/**
 * Possibly initiates and/or completes termination.
 *
 * @param now 如果true,則無條件終止,否則會等待沒有work且沒有活動worker時才終止
* @param enable 表示是否允許關閉,爲false表示當runstate爲非SHUTDOWN狀態時,將不會關閉。若爲true,且
* now爲false,則如果發現還有活動的worker或未執行完的任務,則不會關閉
 * @return true 表示正在關閉或者已經關閉
 */
private boolean tryTerminate(boolean now, boolean enable) {
    int rs;
    if (this == common)                       // cannot shut down
        return false;
    if ((rs = runState) >= 0) {
        if (!enable)
            return false;
        rs = lockRunState();                  // enter SHUTDOWN phase
        unlockRunState(rs, (rs & ~RSLOCK) | SHUTDOWN);
    }
   //這裏主要是檢測是否有其他線程執行了tryTerminate,並且已經進入stop階段。因爲其他線程也會調用
	//awaitWork並因此調用tryTerminate。如果沒有設置STOP,則進入SHUTDOWN階段。整個終結過程被分爲
	// SHUTDOWN和stop兩個階段,SHUTDOWN之後就是stop。SHUTDOWN主要是對剩餘任務的處理,如果now爲false
	//則SHUTDOWN階段會盡快把剩餘任務執行完畢,否則忽略這些任務進入stop階段。且設置SHUTDOWN後,pool
	//不再允許外部推送任務。
    if ((rs & STOP) == 0) {
        if (!now) {                           // check quiescence
            for (long oldSum = 0L;;) {        // repeat until stable
                WorkQueue[] ws; WorkQueue w; int m, b; long c;
                long checkSum = ctl;
                if ((int)(checkSum >> AC_SHIFT) + (config & SMASK) > 0)
                    return false;             // still active workers
                if ((ws = workQueues) == null || (m = ws.length - 1) <= 0)
                    break;                    // check queues
		//遍歷隊列,如果隊列有task,則嘗試激活一個worker來執行task
                for (int i = 0; i <= m; ++i) {
                    if ((w = ws[i]) != null) {
                        if ((b = w.base) != w.top || w.scanState >= 0 ||
                            w.currentSteal != null) {
                            tryRelease(c = ctl, ws[m & (int)c], AC_UNIT);
                            return false;     // arrange for recheck
                        }
		//如果隊列不爲空,checksum就會變
                        checkSum += b;
                        if ((i & 1) == 0)
                            w.qlock = -1;     // try to disable external
                    }
                }
		//如果所有隊列都爲空,則break。分析下oldSum == (oldSum = checkSum)成立的條件:
		//剛剛開始checksum=ctl,oldSum=0,如果此時ctl=0,條件將成立,但這是不可能的,因爲
		//ctl=0,則ctl的SP部分爲0,AC爲0,TC也爲0,但是AC爲0表示還有活躍的worker,所以
		//在前面會return false,代碼到不了這裏。所以第一次到達這裏時,條件一定不成立,一定會
		//進行新一輪循環。從第二次循環開始,如果發現不爲空的隊列,checksum就會改變,再次導致條件
		//不成立。所以當這個條件成立時,意味着至少執行了2次循環,且最後一次循環時,所有隊列都爲空
                if (oldSum == (oldSum = checkSum))
                    break;
            }
        }
        if ((runState & STOP) == 0) {
            rs = lockRunState();              // enter STOP phase
            unlockRunState(rs, (rs & ~RSLOCK) | STOP);
        }
    }
    //pass表示循環執行了幾輪,第一輪是0,第二輪pass是1,以此類推。如果沒有退出循環,
	//第一輪僅僅是設置所有工作隊列的qlock爲-1,表示terminate,然後進入第二輪,取消所有隊列的任務
	//並嘗試激活所有休眠中的worker,然後進入第三輪,中斷worker的owner對應的線程。
	//第四輪結束tryTerminate
    int pass = 0;                             // 3 passes to help terminate
    for (long oldSum = 0L;;) {                // or until done or stable
        WorkQueue[] ws; WorkQueue w; ForkJoinWorkerThread wt; int m;
        long checkSum = ctl;
        if ((short)(checkSum >>> TC_SHIFT) + (config & SMASK) <= 0 ||
            (ws = workQueues) == null || (m = ws.length - 1) <= 0) {
            if ((runState & TERMINATED) == 0) {
                rs = lockRunState();          // done
                unlockRunState(rs, (rs & ~RSLOCK) | TERMINATED);
                synchronized (this) { notifyAll(); } // for awaitTermination
            }
            break;
        }
	
        for (int i = 0; i <= m; ++i) {
            if ((w = ws[i]) != null) {
                checkSum += w.base;
                w.qlock = -1;                 // try to disable
                if (pass > 0) {
                    w.cancelAll();            // clear queue
                    if (pass > 1 && (wt = w.owner) != null) {
                        if (!wt.isInterrupted()) {
                            try {             // unblock join
                                wt.interrupt();
                            } catch (Throwable ignore) {
                            }
                        }
                        if (w.scanState < 0)
			//這裏之所以要把線程喚醒,是因爲有些很多操作採用非阻塞的策略,所以調用
			// interrupt可能是無法真正中斷線程的,故需要將線程喚醒,讓其看到中斷
			//標記自行終結
                            U.unpark(wt);     // wake up
                    }
                }
            }
        }
	//當前面存在不爲空的工作隊列時,就會checkSum != oldSum
        if (checkSum != oldSum) {             // unstable
            oldSum = checkSum;
            pass = 0;
        }
        else if (pass > 3 && pass > m)        // can't further help
            break;
        else if (++pass > 1) {                // try to dequeue
            long c; int j = 0, sp;            // bound attempts
            while (j++ <= m && (sp = (int)(c = ctl)) != 0)
                tryRelease(c, ws[sp & m], AC_UNIT);
        }
    }
    return true;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章