ForkJoinPool
是 Java 7 引进的一个并发东西类,它是根据“使命分割”(task splitting)的并行核算完结的,特别适用于核算密集型、数据量大的场景。在本文中,我们将会深入了解 ForkJoinPool
的完结原理,从源码视点分析其内部结构、作业机制和算法完结,希望对读者有所帮助。
1. 概述
ForkJoinPool
是一个线程池的完结,它继承了 AbstractExecutorService
抽象类,完结了 ExecutorService
接口和 Executor
接口。ForkJoinPool
最首要的特点是采用了作业盗取(work-stealing)算法来完结使命的调度和履行,能够更好地使用多核处理器的核算资源。
在 ForkJoinPool
中,一切使命都被分为更小的子使命,这些子使命被递归地划分成更小的子使命,直到不能再划分停止。然后将这些子使命分配给线程池中的闲暇线程履行,假如某个线程的子使命履行完了,而其他线程的子使命还没有履行完,那么它就会从其他线程的行列中盗取一个子使命履行。这样做的优点是能够防止线程由于履行深重的使命而阻塞,使得多个线程之间的负载愈加均衡。
2. 内部结构
ForkJoinPool
的内部结构十分复杂,首要包括以下几个组成部分:
2.1 WorkQueue
WorkQueue
是 ForkJoinPool
中最重要的组件之一,它用于保存每个线程的使命行列。每个线程都有一个独立的使命行列,使命行列的长度为 2 的次幂(默以为 8192),当行列中的使命数量到达必定阈值时,线程会测验将自己的使命行列分割成两个行列,以便更好地支撑作业盗取算法。
2.2 ForkJoinTask
ForkJoinTask
是 ForkJoinPool
中使命的抽象类,一切的使命都必须继承自 ForkJoinTask
,它包含了两个重要的办法:fork()
和 join()
。
fork()
办法用于将一个使命分化成更小的子使命,并将子使命提交到线程池中履行。join()
办法则用于等候一个使命履行完结并回来其成果。ForkJoinTask
还供给了一些其他的办法,例如 isCompletedAbnormally()
和 getException()
,用于处理反常。
2.3 WorkStealingQueue
WorkStealingQueue
是 ForkJoinPool
中的另一个重要组件,它被用来完结作业盗取算法。WorkStealingQueue
是一个双端行列,用于保存其他线程的使命。每个线程都会保护一个自己的 WorkStealingQueue
,当一个线程的使命行列为空时,它就会测验从其他线程的 WorkStealingQueue
中盗取一个使命履行。
2.4 WorkStealingTask
WorkStealingTask
是 ForkJoinPool
中使命的详细完结类,它继承自 ForkJoinTask
,并且完结了 Runnable
接口。WorkStealingTask
用于封装使命的履行逻辑,它会被提交到线程池中履行,假如履行过程中需求分化成更小的子使命,那么它就会调用 fork()
办法将子使命提交到线程池中履行。
2.5 WorkQueuePool
WorkQueuePool
是 ForkJoinPool
的另一个重要组件,它用于管理线程池中的一切线程和使命行列。WorkQueuePool
中保护了一个线程数组和一个使命行列数组,每个线程都会被分配一个独立的使命行列。WorkQueuePool
还供给了一些办法,用于向线程池中提交使命和封闭线程池。
3. 作业机制
ForkJoinPool
的作业机制十分复杂,但能够简略概括为以下几个步骤:
- 当一个使命被提交到线程池时,它会被划分成更小的子使命,并将子使命分配到不同的线程的使命行列中。
- 线程从自己的使命行列中取出使命履行,假如行列为空,那么线程就会从其他线程的使命行列中盗取一个使命履行,直到自己的使命行列为空停止。
- 假如一个使命需求被分化成更小的子使命,那么它就会调用
fork()
办法将子使命提交到线程池中履行,并等候子使命履行完结。 - 假如一个线程的使命行列中的使命数量到达必定阈值,那么线程就会测验将自己的使命行列分割成两个行列,以便更好地支撑作业盗取算法。
- 当一切使命履行完结后,线程池会封闭并释放资源。
4. 算法完结
ForkJoinPool
的作业机制是根据作业盗取算法完结的,这是一种优异的并发算法,能够更好地使用多核处理器的核算资源,进步使命履行功率。
作业盗取算法的基本思想是:每个线程都有自己的使命行列,当线程履行完自己行列中的使命后,会从其他线程的使命行列中“盗取”一个使命履行,这样能够更好地使用多核处理器的核算资源。详细完结方式是,每个线程都会保护一个自己的使命行列(WorkStealingQueue
),线程从自己的行列中取使命履行,假如行列为空,就会从其他线程的行列中“盗取”一个使命履行。
当一个线程需求“盗取”使命时,它会选择另一个线程的使命行列中最靠近队尾的使命履行(由于这个使命最可能是最新的使命,需求更快地得到履行)。线程“盗取”使命时需求遵循以下原则:
- 只从队尾“盗取”使命,即只履行最靠近队尾的使命;
- 只从其他线程的使命行列中“盗取”使命,不从自己的使命行列中“盗取”使命,以防止死锁;
- 使命行列为空时,不履行“盗取”操作,以防止线程之间抢夺资源。
在 ForkJoinPool
中,每个线程保护一个 WorkStealingQueue
,线程池会将使命分配给不同的线程,并将使命分化成更小的子使命分配到不同的线程的使命行列中。线程履行使命时,会从自己的使命行列中取使命履行,假如行列为空,就会从其他线程的行列中“盗取”一个使命履行。假如一个使命需求被分化成更小的子使命,那么它就会调用 fork()
办法将子使命提交到线程池中履行,并等候子使命履行完结。
5. 总结
ForkJoinPool
是 Java 并发编程中十分重要的一个东西,它能够更好地使用多核处理器的核算资源,进步使命履行功率。ForkJoinPool
的作业机制根据作业盗取算法完结,每个线程都保护一个自己的使命行列,并从其他线程的行列中“盗取”使命履行,以防止线程之间抢夺资源。
在实际使用 ForkJoinPool
时,需求留意以下几点:
- 使命分化的粒度要恰当,过小会增加线程之间的通讯开销,过大会导致使命的分配不均衡;
- 关于不同类型的使命,需求选择不同的使命分化策略,以更好地使用多核处理器的核算资源;
- 在使命履行时,需求留意防止线程之间的竞赛和死锁等问题。
总之,ForkJoinPool
是一个十分强大的东西,能够更好地使用多核处理。
没有重视的小伙伴点点重视喔,后边还有更精彩的源码解读文章,想了解《Netty结构详解:高性能网络编程的设计与完结》,能够通过/post/722143… 衔接拜访过往的精彩内容。