初步了解ForkJoin分支合并框架

看一下类图

初步了解ForkJoin分支合并框架_第1张图片
初步了解ForkJoin分支合并框架_第2张图片
Fork/Join是java在1.7之后加入的一个并行计算框架,他采用了分治的思想,把一个大任务分成若干个小任务,并子任务又有可能进行进行拆分,最后计算完成后进行结果合并,整个过程是递归的过程。Fork是将一个任务进行拆分成子任务,子任务进行计算,Join则是把子任务计算的结果,进合并。

实例

package jdk8;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

/**
 * @Author:苏牧夕
 * @Date:2020/3/19 1:41
 * @Version 1.0
 */
public class ForkJoin {
    class MyTask2 extends ForkJoinTask{

        @Override
        public Integer getRawResult() {
            return null;
        }

        @Override
        protected void setRawResult(Integer value) {

        }

        @Override
        protected boolean exec() {
            return false;
        }
    }
    static class MyTask extends RecursiveTask{
        private static final Integer ABJUST_VALUE = 10;
        private int begin;
        private int end;
        private int result;
        public MyTask(int begin, int end) {
            this.begin = begin;
            this.end = end;
        }

        @Override
        protected Integer compute() {
            if ((end-begin)<=ABJUST_VALUE){
                for (int i= begin; i<=end;i++){
                    result=result+i;
                }
            }else{
                int middle = (end+begin)/2;
                MyTask task1 = new MyTask(begin,middle);
                MyTask task2 = new MyTask(middle+1,end);
                task1.fork();//继续分治
                task2.fork();//继续分支
                result = task1.join()+task2.join();//合并结果
            }
            return result;//返回给上一层合并
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyTask myTask = new MyTask(0,100);
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask submit = forkJoinPool.submit(myTask);
        Integer integer = submit.get();
        System.out.println(integer);
        forkJoinPool.shutdown();

    }
}

ForkJoinPool是也属于一种线程池,只是它的这种线程池与ThreadPoolExecutor有所区别,ForkJoinPool的线程池中每条线程都维护者一个任务队列,当某条线程执行完自己队列中的任务,就会从其他任务队列中窃取任务进行执行,而ThreadPoolExecutor创建的线程池是多条线程维护这一个任务队列,如果任务超出数量,则进行拒绝策略丢弃,除此之外,ForkJoinPool线程池的线程数量通常使用默认就可以,底层会直接获取当前计算机的处理器数量,也可以自定义线程数,而ThreadPoolExecutor则需要手段获取计算机核数了进行初始化线程。

ForkJoin中的几大核心类

  1. ForkJoinPool执行任务的线程池,继承了AbstractExecutorService类,该线程池是通过DefaultForkJoinWorkerThreadFactory或者InnoCuousForkJoinWorkerThreadFactory线程工厂产生的工作线程

  2. ForkJoinWorkThread执行任务的工作线程,即ForkJoinPool线程池里面的线程,每个线程都维护者一个双端队列,用于存放内部任务。

  3. ForkJoinTask:任务类,是用于ForkJoinPool的任务抽象类,实现了Future类,方法比较多,所以由出现了两个子类实现了compute()方法

    1. RecurisveTask子任务类任务带返回结构是使用该类

    2. RecursiveACtion子任务类任务不带返回结构时使用

      compute实现逻辑一般都是基于分治思想

      if 任务足够小
          直接返回结构
       else 
           进行分割子任务
           依次调用每个子任务的fork方法执行子任务
           依次调用每个子任务的join方法合并执行结构。
           
      /**
       * @Author:苏牧夕
       * @Date:2020/3/19 1:41
       * @Version 1.0
       */
      public class ForkJoin {
          class MyTask2 extends ForkJoinTask{
      
              @Override
              public Integer getRawResult() {
                  return null;
              }
      
              @Override
              protected void setRawResult(Integer value) {
      
              }
      
              @Override
              protected boolean exec() {
                  return false;
              }
          }
          static class MyTask extends RecursiveTask{
              private static final Integer ABJUST_VALUE = 10;
              private int begin;
              private int end;
              private int result;
              public MyTask(int begin, int end) {
                  this.begin = begin;
                  this.end = end;
              }
      
              @Override
              protected Integer compute() {
                  if ((end-begin)<=ABJUST_VALUE){
                      for (int i= begin; i<=end;i++){
                          result=result+i;
                      }
                  }else{
                      int middle = (end+begin)/2;
                      MyTask task1 = new MyTask(begin,middle);
                      MyTask task2 = new MyTask(middle+1,end);
                      task1.fork();
                      task2.fork();
                      result = task1.join()+task2.join();
                  }
                  return result;
              }
          }
      
          public static void main(String[] args) throws ExecutionException, InterruptedException {
      
              MyTask myTask = new MyTask(0,100);
      //        ForkJoinPool forkJoinPool = new ForkJoinPool();
              ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
              ForkJoinTask submit = forkJoinPool.submit(myTask);
              Integer integer = submit.get();
              System.out.println(integer);
              forkJoinPool.shutdown();
      
          }
      }
        
      

      (图片引用与网络,具体谋篇博客忘记了)
      初步了解ForkJoin分支合并框架_第3张图片

ForkJoinPool中的每个工作线程ForkJoinWorkerThread都对应着一个任务队列WorkQueue, 工作线程优先处理来至自身队列的任务(LIFO或者FIFO顺序),这种顺序由参数mode决定,然后以FIFO的顺序随机窃取其他队列中的任务
初步了解ForkJoin分支合并框架_第4张图片

ForkJoinPool中的任务分为两种:一种是外界线程提交的任务(如s执行umit或者execute提交的任务),另外一种是fork出来的子任务(Worker task) 。两种任务的会放在WorkQueue数组中,但是这两种任务并不会混合在同一个队列里面,ForkJoinPool内部使用了一种随机哈希算法将工作队列与对应的工作线程关联起来,submission任务存储在WorkQueue数组的偶数索引位置,Workertask任务存放在奇数索引的位置上。实质上二者的2一样,只是他们都被限制只能执行他们提交的本地任务。

ForkJoinPool中的线程优点:

  • 线程是不会因为等待某个子任务的完成或者没有内部任务要执行而被阻塞等待、挂起、而是会扫描所有的队列,窃取任务,知道所有队列都为空时,才会被挂起。
  • Fork-join框架在多cpu的环境下,能提供很好的并行性能,在使用普通线程池ThreadPoolExecutor的情况下,当cpu不再是性能瓶颈的时候,能并行地允许多个线程,然而却因为要互斥访问一个任务队列导致性能提高不了,而ForkJoinPool线程池为每个线程维护着一个内部任务队列,已经一个全局任务队列,而且任务队列都是双端队列,可从首尾两端来获取任务,极大地减少了竞争的可能性,提高并行的性能。

异步回调

  • CompletableFuture

  • 实例

    /**
     * @Author:苏牧夕
     * @Date:2020/3/19 13:45
     * @Version 1.0
     */
    public class CompletableFutureDemo {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //异步没返回值
            CompletableFuture voidCompletableFuture = CompletableFuture.runAsync(() -> {
                System.out.println(Thread.currentThread().getName() + "没有返回值");
            });
            voidCompletableFuture.get();
            //异步有返回值
            CompletableFuture integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
                System.out.println(Thread.currentThread().getName() + "模拟查询");
                int a = 10/0;
                return 1024;
            });
    
            integerCompletableFuture
                    //正常走这条分支
                    .whenComplete((t,u)->{
                System.out.println("***t:"+t);
                System.out.println("****u:"+u);
            })//完成异常则走下面分支
            .exceptionally(e->{
                System.out.println("exception:"+e.getMessage());
                return 4444;
            }).get();
        }
    }
    
    

你可能感兴趣的:(Java系列)