ForkJoinPool 是 Java 7 引进的一个并发东西类,它是根据“使命分割”(task splitting)的并行核算完结的,特别适用于核算密集型、数据量大的场景。在本文中,我们将会深入了解 ForkJoinPool 的完结原理,从源码视点分析其内部结构、作业机制和算法完结,希望对读者有所帮助。

1. 概述

ForkJoinPool 是一个线程池的完结,它继承了 AbstractExecutorService 抽象类,完结了 ExecutorService 接口Executor 接口。ForkJoinPool 最首要的特点是采用了作业盗取(work-stealing)算法来完结使命的调度和履行,能够更好地使用多核处理器的核算资源。

ForkJoinPool 中,一切使命都被分为更小的子使命,这些子使命被递归地划分成更小的子使命,直到不能再划分停止。然后将这些子使命分配给线程池中的闲暇线程履行,假如某个线程的子使命履行完了,而其他线程的子使命还没有履行完,那么它就会从其他线程的行列中盗取一个子使命履行。这样做的优点是能够防止线程由于履行深重的使命而阻塞,使得多个线程之间的负载愈加均衡。

2. 内部结构

ForkJoinPool 的内部结构十分复杂,首要包括以下几个组成部分:

2.1 WorkQueue

WorkQueueForkJoinPool 中最重要的组件之一,它用于保存每个线程的使命行列。每个线程都有一个独立的使命行列,使命行列的长度为 2 的次幂(默以为 8192),当行列中的使命数量到达必定阈值时,线程会测验将自己的使命行列分割成两个行列,以便更好地支撑作业盗取算法。

2.2 ForkJoinTask

ForkJoinTaskForkJoinPool 中使命的抽象类,一切的使命都必须继承自 ForkJoinTask,它包含了两个重要的办法:fork()join()

fork() 办法用于将一个使命分化成更小的子使命,并将子使命提交到线程池中履行。join() 办法则用于等候一个使命履行完结并回来其成果。ForkJoinTask 还供给了一些其他的办法,例如 isCompletedAbnormally()getException(),用于处理反常。

2.3 WorkStealingQueue

WorkStealingQueueForkJoinPool 中的另一个重要组件,它被用来完结作业盗取算法。WorkStealingQueue 是一个双端行列,用于保存其他线程的使命。每个线程都会保护一个自己的 WorkStealingQueue,当一个线程的使命行列为空时,它就会测验从其他线程的 WorkStealingQueue 中盗取一个使命履行。

2.4 WorkStealingTask

WorkStealingTaskForkJoinPool 中使命的详细完结类,它继承自 ForkJoinTask,并且完结了 Runnable 接口。WorkStealingTask 用于封装使命的履行逻辑,它会被提交到线程池中履行,假如履行过程中需求分化成更小的子使命,那么它就会调用 fork() 办法将子使命提交到线程池中履行。

2.5 WorkQueuePool

WorkQueuePoolForkJoinPool 的另一个重要组件,它用于管理线程池中的一切线程和使命行列。WorkQueuePool 中保护了一个线程数组和一个使命行列数组,每个线程都会被分配一个独立的使命行列。WorkQueuePool 还供给了一些办法,用于向线程池中提交使命和封闭线程池。

3. 作业机制

ForkJoinPool 的作业机制十分复杂,但能够简略概括为以下几个步骤:

  1. 当一个使命被提交到线程池时,它会被划分成更小的子使命,并将子使命分配到不同的线程的使命行列中。
  2. 线程从自己的使命行列中取出使命履行,假如行列为空,那么线程就会从其他线程的使命行列中盗取一个使命履行,直到自己的使命行列为空停止。
  3. 假如一个使命需求被分化成更小的子使命,那么它就会调用 fork() 办法将子使命提交到线程池中履行,并等候子使命履行完结。
  4. 假如一个线程的使命行列中的使命数量到达必定阈值,那么线程就会测验将自己的使命行列分割成两个行列,以便更好地支撑作业盗取算法。
  5. 当一切使命履行完结后,线程池会封闭并释放资源。

4. 算法完结

ForkJoinPool 的作业机制是根据作业盗取算法完结的,这是一种优异的并发算法,能够更好地使用多核处理器的核算资源,进步使命履行功率

作业盗取算法的基本思想是:每个线程都有自己的使命行列,当线程履行完自己行列中的使命后,会从其他线程的使命行列中“盗取”一个使命履行,这样能够更好地使用多核处理器的核算资源。详细完结方式是,每个线程都会保护一个自己的使命行列(WorkStealingQueue),线程从自己的行列中取使命履行,假如行列为空,就会从其他线程的行列中“盗取”一个使命履行。

当一个线程需求“盗取”使命时,它会选择另一个线程的使命行列中最靠近队尾的使命履行(由于这个使命最可能是最新的使命,需求更快地得到履行)。线程“盗取”使命时需求遵循以下原则:

  1. 只从队尾“盗取”使命,即只履行最靠近队尾的使命;
  2. 只从其他线程的使命行列中“盗取”使命,不从自己的使命行列中“盗取”使命,以防止死锁;
  3. 使命行列为空时,不履行“盗取”操作,以防止线程之间抢夺资源。

ForkJoinPool 中,每个线程保护一个 WorkStealingQueue,线程池会将使命分配给不同的线程,并将使命分化成更小的子使命分配到不同的线程的使命行列中。线程履行使命时,会从自己的使命行列中取使命履行,假如行列为空,就会从其他线程的行列中“盗取”一个使命履行。假如一个使命需求被分化成更小的子使命,那么它就会调用 fork() 办法将子使命提交到线程池中履行,并等候子使命履行完结。

5. 总结

ForkJoinPool 是 Java 并发编程中十分重要的一个东西,它能够更好地使用多核处理器的核算资源,进步使命履行功率。ForkJoinPool 的作业机制根据作业盗取算法完结,每个线程都保护一个自己的使命行列,并从其他线程的行列中“盗取”使命履行,以防止线程之间抢夺资源。

在实际使用 ForkJoinPool 时,需求留意以下几点:

  1. 使命分化的粒度要恰当,过小会增加线程之间的通讯开销,过大会导致使命的分配不均衡;
  2. 关于不同类型的使命,需求选择不同的使命分化策略,以更好地使用多核处理器的核算资源;
  3. 在使命履行时,需求留意防止线程之间的竞赛和死锁等问题。

总之,ForkJoinPool 是一个十分强大的东西,能够更好地使用多核处理。

没有重视的小伙伴点点重视喔,后边还有更精彩的源码解读文章,想了解《Netty结构详解:高性能网络编程的设计与完结》,能够通过/post/722143… 衔接拜访过往的精彩内容。