ForkJoin全解1:簡單使用與大致實現原理

1、 使用示例

import java.lang.reflect.Method;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ThreadLocalRandom;

public class ForkJoinDemo {
   public static void main(String[] args) throws Exception{
         //使用ForkJoinPool來執行任務
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        //生成一個計算任務,負責計算1+2+3+4
        CountTaskTmp task = new CountTaskTmp(1, 100000000);
        long r = forkJoinPool.invoke(task);
        System.out.println(r);
    }
}


@SuppressWarnings("serial")

// RecursiveTask是ForkJoinTask的子類
class CountTaskTmp extends RecursiveTask<Long> {
    private static final long THRESHOLD = 10000000;
    private long start;
    private long end;

    public CountTaskTmp(long start, long end) {
        this.start = start;
        this.end = end;
    }

    //實現compute 方法來實現任務切分和計算
    @Override
    protected Long compute() {
        long sum = 0;
        boolean canCompute = (end - start) <= THRESHOLD;
        if (canCompute) {
            for (long i = start; i <= end; i++)
                sum += i;
        } else {
            //如果任務大於閥值,就分裂成兩個子任務計算
            long mid = (start + end) / 2;
           
            CountTaskTmp leftTask = new CountTaskTmp(start, mid);
            CountTaskTmp rightTask = new CountTaskTmp(mid + 1, end);
           
           
            System.out.println("fork開始");
            //執行子任務
            leftTask.fork();
            rightTask.fork();
            System.out.println("fork完畢");
            //等待子任務執行完,並得到結果
            System.out.println("join開始");
            long leftResult = leftTask.join();
            long rightResult = rightTask.join();
            System.out.println("join完畢");
            sum = leftResult + rightResult;
        }

        return sum;
    }
}

 

2、簡寫說明:

下面:

ForkJoinWorkerThread簡寫爲Thread

ForkJoinWorkerPool簡寫爲pool

ForkJoinWorkerTask簡寫爲task

workQueue簡寫爲queue

3、任務竊取大致實現原理

在上面示例中,我們通過THRESHOLD字段指定每個任務計算1千萬個數據,如果超過一千萬,則使用二分法把任務切分爲leftTask和rightTask。如果leftTask和rightTask還是超過一千萬數據,還會繼續切分,當任務足夠小,就開始真正執行計算邏輯。整個過程有點類似遞歸。下面使用僞碼簡單看下工作過程:

class ForkJoinTask{

    fork(){

        forJoinPool.push(this)   //把任務提交到pool執行

    }

T   join(){

        synchronized(this){

            wait();//等待任務執行完畢,任務執行完畢會調用notify

        }

        return getRawResult();//返回當前task的結果

    }

}

class WorkQueue{

    ForkJoinThread owner;

     ForkJoinTask array[];//存放task的數組

     int top=base=0;

     push(task){

          //如果數組長度不夠了,則創建一個長度更大的數組

          growArray()

          //把task放到array

          array[top++]=task

}

//當這個workQueue的所屬線程執行任務時,調用pop獲取task

    pop(){

      return array[top--];

    }

    //當其他線程要竊取這個隊列的任務時,使用poll竊取任務。通過base和top兩個變量,array成了雙向隊列。

    // pop是後進先出,而poll則是先進先出。當隊列的owner執行自身隊列中的task時,通過pop獲得任務,

    //而其他隊列要竊取該隊列的task時,則用該隊列的poll進行任務竊取

    poll(){

      return array[base++];

    }

    growArray(){

          //當array大小不夠時,創建更大的數組,並把舊數組的元素拷貝到新數組

    }

}

class ForkJoinPool{

     WorkQueue workQueues[];//workQueue保存task

     push(task){

          //從workQueues選擇一個工作隊列,將task放入其中

          selectOneWorkQueueFromWorkQueues().add(task)

          signalWork();       //通知worker執行任務

    }

    signalWork(){

        if(線程池每有足夠的活動線程){

               //使用Unsafe的unpark喚醒休眠的線程或調用createWorker創建新線程,並啓動該線程

        }

    }

    createWorker(){

          new ForkJoinThread(this).start();

    }

    // ForkJoinThread構造函數中調用,藉此把新new出來的ForkJoinThread註冊到ForkJoinPool

    registerWorker(ForkJoinThread thread){

          WorkerQueue  workQueue=new WorkerQueue();

          // 簡單起見,這裏暫時理解randomIndex是一個小於workQueues長度的隨機數

          workQueues[randomIndex]=workQueue;

          workQueue.owner=thread

          return workerQueue;

    }

}



class ForkJoinThread{//Thread的子類

     ForkJoinPool pool;

     WorkQueue workQueue;

     ForkJoinThread(ForkJoinPool pool){

        this.pool=pool;

        this. workQueue=pool.registerWorker(this);
    }

    run(){//調用Thread的start方法時,run方法就會被調用

          while(!terminate){

                //不斷執行task,每當任務執行完畢,執行notifyall通知所有等待的worker

               //當自身task執行完就從pool中隨機選擇workQueue,並竊取該workQueue的任務來執行

                //如果竊取不到任務,且當前worker由於調用了join,在等待其他線程的任務完成,則當前線程休眠

                //如果竊取不到任務,且無需等待其他線程任務的執行,則線程終止

            }

    }

}

 

 

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