ForkJoinPool源码解析(未写完,待补充)

╄→гoц情女王★ 提交于 2019-11-25 23:02:59

目录

  1. ForkJoinPool介绍
    • 什么是ForkJoinPool?
    • 相对ThreadPoolExecutor有什么优缺点?
    • 什么时候使用ForkJoinPool?
  2. 实例DEMO
  3. 涉及核心类或组件
    • ForkJoinPool
    • ForkJoinTask
    • ForkJoinWorkerThread
    • WorkQueue
  4. 执行流程
    • 执行流程
    • 任务窃取
  5. 源码分析
    • 添加任务
    • fork任务
    • join任务
  6. 补充部分

详情

1. ForkJoinPool介绍

1.1 什么是ForkJoinPool?

ForkJoinPool是在JDK1.7新添加的一个JUC线程池工具类,它同样继承了AbstractExcutorService,如下图所示

在未开始介绍该类之前,我们先从字面意思猜想一下该类的左右:fork:分叉;join:参加、联合、连接;pool:小池;翻译:参加分叉的小池,或者连接分叉的小池。恩,,,这个翻译坑爹呀!!!!

不不不,不是这样的,咱们可以适当的加点猜想嘛,比如说在JUC中,一定与并发有关系,那么可否这么翻译呢?

“一个将任务拆分并结果合并的线程池”。

1.2 相对ThreadPoolExecutor有什么优缺点?

从上文我们已经知道ForkJoinPool是一个线程池,那么他与咱们常用的 ThreadPoolExecutor 线程池有什么区别呢? 首先看下两个线程池的结构图:

接着我们分析下:

ThreadPoolExecutor 对于任务会先交给核心线程去执行,如果核心线程数不够则会放入队列,线程池中的线程统一去任务队列中获取任务并执行,我们可以从这样的设计中很明显的看出来:ThreadPoolExecutor执行时间不确定的任务类型,比如说:网络IO操作、定时任务等

ForkJoinPool 则是一个线程对应一个任务队列,每个任务正常来说只需要执行自己的任务;如果线程对应的任务列表为空的时候,会在随机窃取其他线程任务列表中的任务(为减少获取任务竞争,会从任务另一端获取任务),当然上图没有表示出来。这样既减少了任务竞争,又能充分利用CPU资源。我们可以从这样的设计中很明显的看出来:ForkJoinPool非常适合执行任务比较多、执行时间比较短的程序,比如过滤集合中的元素(JDK1.8 stream底层就是ForkJoinPool哟)

最后补充: ForkJoinPool 的出现不是为了替换ThreadPoolExecutor这一类的线程池,而是为了做功能上的补充,两者各有使用场景,根据不同的场景使用不同的线程池即可

1.3 什么时候使用ForkJoinPool?

  • 引用 1.2 中ForkJoinPool 分析和补充部分:ForkJoinPool非常适合执行任务比较多、执行事件比较短的程序,比如过滤集合中的元素(JDK1.8 stream底层就是ForkJoinPool哟);
  • ForkJoinPool 的出现不是为了替换ThreadPoolExecutor这一类的线程池,而是为了做功能上的补充,两者各有使用场景,根据不同的场景使用不同的线程池即可。

2 实例DEMO

接下里这里就以过滤为本中 违禁字 为例(现实中肯定使用违禁词了,本文只是做简单DEMO),先说下具体的测试方法:这里有一串文本和已知的违禁字集合,如果文本中的文字在违禁字集合中,则计算违禁字出现的次数并返回

// 测试方法 public class ForkJoinPoolLearn {     public final static String CONTENT = "哇,好帅哟!哇,是啊,我好喜欢呢!哇,可否给个签名呢?";     public static final int THRESHHOLD = 5;     public static List<String> BLACK_WORDS = new ArrayList<>();      static {         BLACK_WORDS.add("哇");     }      public static void main(String[] args) {         //使用ForkJoinPool来执行任务         // 有返回值对象         System.out.println("即将测试有返回值对象。。。");         ForkJoinPool forkJoinPool = new ForkJoinPool();         MyRecursiveTask myRecursiveTask = new MyRecursiveTask(0, ForkJoinPoolLearn.CONTENT.length(), Arrays.asList(ForkJoinPoolLearn.CONTENT.split("")));         Integer value = forkJoinPool.invoke(myRecursiveTask);         System.out.println(String.format("字符串:%s 中包含违禁词数量:%s,违禁词:%s", CONTENT, value, StringUtils.join(BLACK_WORDS, ",")));     } }  // 提交任务类 public class MyRecursiveTask extends RecursiveTask<Integer> {      private int startIndex;     private int endIndex;     private List<String> words;      public MyRecursiveTask(int startIndex, int endIndex, List<String> words) {         this.startIndex = startIndex;         this.endIndex = endIndex;         this.words = words;     }      @Override     protected Integer compute() {         int sum = 0;         if ((endIndex - startIndex) <= ForkJoinPoolLearn.THRESHHOLD) {// 如果长度不可再分割,则开始做过滤             for (int i = startIndex; i < words.size() && i < endIndex; i++) {                 String word = words.get(i);                 if (ForkJoinPoolLearn.BLACK_WORDS.contains(word)) {                     sum += 1;                 }             }         } else {// 如果长度过长,fork为两个任务来处理             int middle = (startIndex + endIndex) / 2;             MyRecursiveTask left = new MyRecursiveTask(startIndex, middle, words);             MyRecursiveTask right = new MyRecursiveTask(middle, endIndex, words);             left.fork();             right.fork();             Integer leftValue = left.join();             Integer rightValue = right.join();             sum = leftValue + rightValue;         }         return sum;// 返回计算后的值     } } 

以上示例返回的结果:“字符串:哇,好帅哟!哇,是啊,我好喜欢呢!哇,可否给个签名呢? 中包含违禁词数量:3,违禁词:哇”

3. 涉及核心类或组件

3.1 ForkJoinPool

该类为ExecutorService的实现类,主要负责工作线程的管理、任务队列的维护,以及控制整个任务调度流程;

构造方法: 对外提供了三种构造方法:

  1. 无参构造
public ForkJoinPool() {     this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),defaultForkJoinWorkerThreadFactory, null, false); } 
  1. 一个int类型构造
public ForkJoinPool(int parallelism) {     this(parallelism, defaultForkJoinWorkerThreadFactory, null, false); } 
  1. 多个参数的构造,代码如下:
public ForkJoinPool(int parallelism,                     ForkJoinWorkerThreadFactory factory,                     UncaughtExceptionHandler handler,                     boolean asyncMode) {     this(checkParallelism(parallelism),checkFactory(factory),handler,asyncMode ? FIFO_QUEUE : LIFO_QUEUE,"ForkJoinPool-" + nextPoolId() + "-worker-");     checkPermission(); } 

其实以上三个构造方法最终都是调用的下面的私有构造方法:

private ForkJoinPool(int parallelism,                      ForkJoinWorkerThreadFactory factory,                      UncaughtExceptionHandler handler,                      int mode,                      String workerNamePrefix) {     this.workerNamePrefix = workerNamePrefix;     this.factory = factory;     this.ueh = handler;     this.config = (parallelism & SMASK) | mode;     long np = (long)(-parallelism); // offset ctl counts     this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK); } 

解释一下每个参数的含义:

  • parallelism:并行度,目前在这里暂且解释为线程池的线程数(但这个说法并不完全正确,再看下文会具体解释其含义)
  • factory:创建 ForkJoinWorkerThread 的工厂接口
  • handler:异常处理器
  • mode:取任务的时候是是FIFO还是LIFO模式,0:LIFO;1:FIFO;
  • workerNamePrefix:ForkJoinWorkerThread的名称
  • ctl:线程池的核心控制字段

提交任务方式

  • submit: 有返回值,可提交Runnable,Callable,ForkJoinTask类型任务
  • invoke: 有返回值,可提交ForkJoinTask类型任务,根据不同的 ForkJoinTask 自动判断是否有返回值
  • execute: 无返回值,可提交Runnable,ForkJoinTask类型任务

由于该类实现过于复杂,这里类结构图就不在这里粘贴了,小伙伴可自行打开IDE研究

3.2 ForkJoinTask

该类为Future接口的实现类,fork是其核心方法,用于分解任务并异步执行;而join方法在任务结果计算完毕之后才会运行,用来合并或返回计算结果;其下两个常用实现类:

  • RecursiveAction:表示没有返回结果的ForkJoin任务
  • RecursiveTask:表示具有返回结果的ForkJoin任务,示例DEMO中就是使用该类来提交任务

3.3 ForkJoinWorkerThread

该类为Thread的子类,作为线程池中的工作线程(Worker)执行任务;

线程池中的每个工作线程(ForkJoinWorkerThread)内部都维护一个自己的任务队列(WorkQueue),工作线程优先处理自身队列中的任务(LIFO或FIFO顺序,由线程池构造时的参数 mode 决定),自身队列为空时,以FIFO的顺序随机窃取其它队列中的任务。

3.4 WorkQueue

该类为ForkJoinPool的内部类,用于保存任务;

4. 执行流程

4.1 执行流程

下图为ForkJoinPool的整体执行流程,下文的源码分析会结合该图详细介绍,介绍完毕之后,最后补充具体的方法级别调用图

4.2 任务窃取

待添加

5. 源码分析

到目前为止,大致的执行流程已经明白了,接下来了来具体讲讲源码来了解一下具体的执行过程

5.1 添加任务

待添加 

5.2 fork任务

public final ForkJoinTask<V> fork() {     Thread t;     if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) 	// 当前线程就是 ForkJoinWorkerThread,那么自己fork的任务,自己执行咯。正所谓,自己挖的坑哭着也要填完         ((ForkJoinWorkerThread)t).workQueue.push(this);     else 	// 没人管的任务,那就统一添加到任务队列,最终随机找个线程去执行         ForkJoinPool.common.externalPush(this);     return this; } 

5.3 join任务

public final V join() {     int s;     if ((s = doJoin() & DONE_MASK) != NORMAL) 	// 出错之后,处理下错误         reportException(s);     // 参考添加任务部分     return getRawResult(); } 

补充

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