分支合并-forkjoin

一曲冷凌霜 提交于 2020-03-07 17:40:20

1.分支合并-forkjoin

ForkJoin的框架的基本思想是分而治之。什么是分而治之?分而治之就是将一个复杂的计算,按照设定的阈值进行分解成多个计算,然后将各个计算结果进行汇总。相应的ForkJoin将复杂的计算当做一个任务。而分解的多个计算则是当做一个子任务。

 

 

Fork/Join与传统线程池的区别!

Fork/Join采用“工作窃取模式”,当执行新的任务时他可以将其拆分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随即线程中偷一个并把它加入自己的队列中。

就比如两个CPU上有不同的任务,这时候A已经执行完,B还有任务等待执行,这时候A就会将B队尾的任务偷过来,加入自己的队列中,对于传统的线程,ForkJoin更有效的利用的CPU资源!

测试forkjoin

package com.coding.forkjoin;
​
import java.util.concurrent.RecursiveTask;
​
// 求和
public class ForkJoinWork extends RecursiveTask<Long> {
​
    private Long start;
    private Long end;
    private static final Long tempLong = 10000L; // 临界值
​
    public ForkJoinWork(Long start, Long end) {
        this.start = start;
        this.end = end;
    }
​
    // 计算方法
    @Override
    protected Long compute() {
        // 临界值判断
        if ((end-start)<=tempLong){
            Long sum = 0L;
            for (Long i = start; i <= end ; i++) {
                sum += i;
            }
            return sum;
        }else { // 第二种方式
            long middle = (end + start) / 2;
            ForkJoinWork right = new ForkJoinWork(start,middle);
            right.fork(); // 压入线程队列
            ForkJoinWork left = new ForkJoinWork(middle+1,end);
            left.fork(); // 压入线程队列
​
            // 获得结果 join 会阻塞等待结果
        return right.join() + left.join();
        }
    }
}
package demo.forkjoin;
​
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
​
​
public class MyTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
         test1();
         test2();
         test3();
    }
​
    // 普通的
    private static void test3() {
​
        Long sum = 0L;
​
        long start = System.currentTimeMillis();
​
        for (Long i = 0L; i <= 1000000000L ; i++) {
            sum += i;
        }
​
        long end = System.currentTimeMillis();
​
        System.out.println("times:"+(end-start)+" r=>"+sum);
​
    }
​
    // forkjoin
    private static void test2() throws ExecutionException, InterruptedException {
​
        long start = System.currentTimeMillis();
​
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinWork forkJoinWork = new ForkJoinWork(0L, 1000000000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(forkJoinWork);
        Long sum = submit.get();
​
        long end = System.currentTimeMillis();
​
        System.out.println("times:"+(end-start)+" r=>"+sum);
    }
​
    // 并行流计算
    private static void test1() {
​
        long start = System.currentTimeMillis();
        // 流计算
        long sum = LongStream.rangeClosed(0L, 1000000000L).parallel().reduce(0, Long::sum);
​
        long end = System.currentTimeMillis();
​
        System.out.println("times:"+(end-start)+" r=>"+sum);
    }
}

这里把代码给大家,大家可以自行测试,我电脑可能是配置问题,导致普通的算的比较快

注:

使用ForkJoin将相同的计算任务通过多线程的进行执行。从而能提高数据的计算速度。但是我们需要注意:

  • 使用这种多线程带来的数据共享问题,在处理结果的合并的时候如果涉及到数据共享的问题,我们尽可能使用JDK为我们提供的并发容器。

  • 在使用JVM的时候我们要考虑OOM的问题,如果我们的任务处理时间非常耗时,并且处理的数据非常大的时候。会造成OOM。

  • ForkJoin也是通过多线程的方式进行处理任务。那么我们不得不考虑是否应该使用ForkJoin。因为当数据量不是特别大的时候,我们没有必要使用ForkJoin。因为多线程会涉及到上下文的切换。所以数据量不大的时候使用串行比使用多线程快。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!