多线程使用forkJoin进行任务拆分

什么是Fork/Join框架

    Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。

     我们再通过Fork和Join这两个单词来理解下Fork/Join框架,Fork就是把一个大任务切分为若干子任务并行的执行,Join就是合并这些子任务的执行结果,最后得到这个大任务的结果。比如计算1+2+。。+10000,可以分割成10个子任务,每个子任务分别对1000个数进行求和,最终汇总这10个子任务的结果。Fork/Join的运行流程图如下:

在这里插入图片描述

二、工作窃取算法

工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。
我们需要做一个比较大的任务,我们可以把这个任务分割为若干互不依赖的子任务,为了减少线程间的竞争,于是把这些子任务分别放到不同的队列里,并为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应,比如A线程负责处理A队列里的任务。但是有的线程会先把自己队列里的任务干完,而其他线程对应的队列里还有任务等待处理。干完活的线程与其等着,不如去帮其他线程干活,于是它就去其他线程的队列里窃取一个任务来执行。而在这时它们会访问同一个队列,所以为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列,被窃取任务线程永远从双端队列的头部拿任务执行,而窃取任务的线程永远从双端队列的尾部拿任务执行。
工作窃取算法的优点是充分利用线程进行并行计算,并减少了线程间的竞争,其缺点是在某些情况下还是存在竞争,比如双端队列里只有一个任务时。并且消耗了更多的系统资源,比如创建多个线程和多个双端队列。
在这里插入图片描述
1.ForkJoinPool 的每个工作线程都维护着一个工作队列(WorkQueue),这是一个双端队列(Deque),里面存放的对象是任务(ForkJoinTask)。
2.每个工作线程在运行中产生新的任务(通常是因为调用了 fork())时,会放入工作队列的队尾,并且工作线程在处理自己的工作队列时,使用的是 LIFO 方式,也就是说每次从队尾取出任务来执行。
3.每个工作线程在处理自己的工作队列同时,会尝试窃取一个任务(或是来自于刚刚提交到 pool 的任务,或是来自于其他工作线程的工作队列),窃取的任务位于其他线程的工作队列的队首,也就是说工作线程在窃取其他工作线程的任务时,使用的是 FIFO 方式。
4.在遇到 join() 时,如果需要 join 的任务尚未完成,则会先处理其他任务,并等待其完成。
5.在既没有自己的任务,也没有可以窃取的任务时,进入休眠

三、fork/join的使用

ForkJoinTask:我们要使用 ForkJoin 框架,必须首先创建一个 ForkJoin 任务。它提供在任务中执行 fork() 和 join() 操作的机制,通常情况下我们不需要直接继承 ForkJoinTask 类,而只需要继承它的子类,Fork/Join 框架提供了以下两个子类:
RecursiveAction:用于没有返回结果的任务。(比如写数据到磁盘,然后就退出了。 一个RecursiveAction可以把自己的工作分割成更小的几块, 这样它们可以由独立的线程或者CPU执行。 我们可以通过继承来实现一个RecursiveAction)
RecursiveTask :用于有返回结果的任务。(可以将自己的工作分割为若干更小任务,并将这些子任务的执行合并到一个集体结果。 可以有几个水平的分割和合并)
CountedCompleter: 在任务完成执行后会触发执行一个自定义的钩子函数

在这里插入图片描述
ForkJoinPool :ForkJoinTask 需要通过 ForkJoinPool 来执行,任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务

有返回值可参考:

没有返回值实例:

public class ExcleController {

 
    public static void main(String[] args) throws InterruptedException {
			//线程池方法操作
       // ExcleController.mdtexcle();
        //任务拆分
        List<PatJsonDto> patJsonDtos = JSONArray.parseArray(pat, PatJsonDto.class);
        System.out.println("kkkk");
        // 创建包含Runtime.getRuntime().availableProcessors()返回值作为个数的并行线程的ForkJoinPool
        Instant start = Instant.now();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        // 提交可分解的PrintTask任务
       // forkJoinPool.submit(new ForkJoinSumCalculate(docMdtExcleVos,0,docMdtExcleVos.size()));
        //模拟患者
       forkJoinPool.submit(new ForkJoinSumCalculate(patJsonDtos,0,patJsonDtos.size()));
        //阻塞当前线程直到 ForkJoinPool 中所有的任务都执行结束
        forkJoinPool.awaitTermination(2, TimeUnit.SECONDS);
        // 关闭线程池
        forkJoinPool.shutdown();
        Instant end = Instant.now();
        System.out.println("单线程耗费时间为:" + Duration.between(start, end).toMillis());

    }
//线程池操作
 public static void mdtexcle(){
//        String pat = HttpUtil.get("10.10.10.66:9999/out/patientOut");
//        List<PatJsonDto> patJsonDtos = JSONArray.parseArray(pat, PatJsonDto.class);
//        System.out.println("kkkk");
//        //患者
//        ThreadUtil.execute(new Runnable() {
//            @Override
//            public void run() {
//                System.out.println("一个线程池方法开始执行----");
//                Instant start = Instant.now();
//
//                inserPat(patJsonDtos);
//                Instant end = Instant.now();
//                System.out.println("单线程耗费时间为:" + Duration.between(start, end).toMillis());
//                System.out.println("一个线程池方法执行结束----");
//            }
//        });
      

    public  static void  inserPat(List<PatJsonDto> list){
        list.forEach(e->{
            System.out.println(e);//添加患者
            //记录添加
        });
    }

  
}
class ForkJoinSumCalculate extends RecursiveAction {

 
    private List<PatJsonDto> list;
    private int start;
    private int end;

    public ForkJoinSumCalculate() {
    }

    public ForkJoinSumCalculate(List<PatJsonDto> list, int start, int end) {
        this.list = list;
        this.start = start;
        this.end = end;
    }

    private static final long THURSHOLD = 500;  //临界值

    public List<PatJsonDto> getList() {
        return list;
    }

    public void setList(List<PatJsonDto> list) {
        this.list = list;
    }

    public long getStart() {
        return start;
    }

    public void setStart(int start) {
        this.start = start;
    }

    public long getEnd() {
        return end;
    }

    public void setEnd(int end) {
        this.end = end;
    }

    @Override
    protected void compute() {
        int length = end - start;
        if(length<THURSHOLD){
     
            ExcleController.inserPat(list);
        }else{
            System.out.println("任务拆解");
            int t=(start+end)/2;
            ForkJoinSumCalculate left = new ForkJoinSumCalculate(list, start, t);
            left.fork();
            ForkJoinSumCalculate right = new ForkJoinSumCalculate(list, t, end);
            right.fork();
        }

    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值