线程池的介绍可以参考我的另一篇文章《Java中线程创建方式》中有提及,这里就不详细赘述,这里主要是对比普通线程与ForkJoinPool的对比。
一、ForkJoinPool
第一次遇见ForkJoinPool还是在Java8的并行流中,并行流的原理就是通过ForkJoinPool创建处理器数目相同(实际上是内核数再减一就是最大线程数)的线程处理并发。话说ForkJoinPool类还是挺复杂的,这里就先简单介绍,后续随着学习深入再进行补充。
1.简介
ForkJoinPool 是JDK 7加入的一个线程池类,在java.util.concurrent 包中。Fork/Join 技术是分治算法(Divide-and-Conquer)的并行实现,它是一项可以获得良好的并行性能的简单且高效的设计技术。目的是为了帮助我们更好地利用多处理器带来的好处,使用所有可用的运算能力来提升应用的性能。我们常用的数组工具类 Arrays 在JDK 8之后新增的并行排序方法(parallelSort)就运用了 ForkJoinPool 的特性,还有 ConcurrentHashMap 在JDK 8之后添加的函数式方法(如forEach等)也有运用。
2.Fork-join任务运行机制
3.算法介绍
(1)分治算法(Divide-and-Conquer):
把任务递归的拆分为各个子任务,这样可以更好的利用系统资源,尽可能的使用所有可用的计算能力来提升应用性能。
(2)work-stealing(工作窃取)算法:
线程池内的所有工作线程都尝试找到并执行已经提交的任务,或者是被其他活动任务创建的子任务(如果不存在就阻塞等待)。这种特性使得 ForkJoinPool 在运行多个可以产生子任务的任务,或者是提交的许多小任务时效率更高。尤其是构建异步模型的 ForkJoinPool 时,对不需要合并(join)的事件类型任务也非常适用。
总结:
在 ForkJoinPool 中,线程池中每个工作线程(ForkJoinWorkerThread)都对应一个任务队列(WorkQueue),工作线程优先处理来自自身队列的任务(LIFO或FIFO顺序,参数 mode 决定),然后以FIFO的顺序随机窃取其他队列中的任务。
可参考:https://www.jianshu.com/p/32a15ef2f1bf
二、Java中的线程池
这里内容就简单介绍,具体可参考我其他关于线程的文章,需要先了解创建线程池的工厂类:Executors。
1.四种常见的基本线程底层都是调用的new ThreadPoolExcutor():
Executors.newFixedThreadPool(nThreads):创建固定大小的线程池。
Executors.newCachedThreadPool():无限线程池。
Executors.newSingleThreadExecutor():创建单个线程的线程池。
Executors.newScheduledThreadPool():创建跑定时任务的线程池。
2.线程池的执行流程:
(1)当任务提交到线程池时,假如当前线程数小于核心线程数,直接创建线程执行,并且不会销毁,直到达到核心线程数。
(2)当核心线程都在执行还有任务提交时,任务放在阻塞队列中。
(3)阻塞队列也满了以后,继续创建线程执行任务,直到达到最大线程数。
(4)最大线程也满了以后,执行对应的拒绝策略。
(5)当线程空闲下来以后,线程在达到线程空闲等待时间后销毁,直至数量降低至核心线程数。
3.线程池相关UML简要类图关系
4.Executor和ExecutorService的区别
ExecutorService 接口继承了 Executor 接口,是 Executor 的子接口
Executor | ExecutorService |
---|---|
Executor 是 Java 线程池的核心接口,用来并发执行提交的任务 | ExecutorService 是 Executor 接口的扩展,提供了异步执行和关闭线程池的方法 |
提供execute()方法用来提交任务 | 提供submit()方法用来提交任务 |
execute()方法无返回值 | submit()方法返回Future对象,可用来获取任务执行结果 |
Executor 接口定义了 execute()方法用来接收一个Runnable接口的对象 | ExecutorService 接口中的 submit()方法可以接受Runnable和Callable接口的对象 |
不能取消任务 | 可以通过Future.cancel()取消pending中的任务 |
没有提供和关闭线程池有关的方法 | 提供了关闭线程池的方法shutdown() |
5.Executors和ThreadPoolExecutor区别
Executors类和ThreadPoolExecutor都是util.concurrent并发包下面的类, Executos下面的newFixedThreadPool、newScheduledThreadPool、newSingleThreadExecutor、newCachedThreadPool底线的实现都是用的ThreadPoolExecutor实现的,所以ThreadPoolExecutor更加灵活。
//newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
//newFixedThreadPool
public newFixedThreadPool(int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//空闲存活时间
TimeUnit unit,//单位
BlockingQueue<Runnable> workQueue,//队列
RejectedExecutionHandler handler) {//拒绝策略
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}