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,在等待其他線程的任務完成,則當前線程休眠
//如果竊取不到任務,且無需等待其他線程任務的執行,則線程終止
}
}
}