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,在等待其他线程的任务完成,则当前线程休眠

                //如果窃取不到任务,且无需等待其他线程任务的执行,则线程终止

            }

    }

}

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值