它实际和 ScheduledThreadPool 线程池非常相似,它只是 ScheduledThreadPool 的一个特例,内部只有一个线程,它只是将 ScheduledThreadPool 的核心线程数设置为了 1。如源码所示:
public static ScheduledExecutorService newSingleThreadScheduledExecutor() { return new DelegatedScheduledExecutorService (new ScheduledThreadPoolExecutor(1)); }上面我们介绍了五种常见的线程池,对于这些线程池我们可以从核心线程数、最大线程数、存活时间三个维度进行一个简单的对比,有利于我们加深对这几种线程池的记忆。
FixedThreadPool SingleThreadExecutor CachedThreadPool ScheduledThreadPool SingleThreadScheduledExecutorcorePoolSize 构造函数传入 1 0 构造函数传入 1
maxPoolSize 同corePoolSize 1 Integer. MAX_VALUE Integer. MAX_VALUE Integer. MAX_VALUE
keepAliveTime 0 0 60 0 0
ForkJoinPool
ForkJoinPool 这是一个在 JDK7 引入的新新线程池,它的主要特点是可以充分利用多核CPU , 可以把一个任务拆分为多个子任务,这些子任务放在不同的处理器上并行执行,当这些子任务执行结束后再把这些结果合并起来,这是一种分治思想。
ForkJoinPool 也正如它的名字一样,第一步进行 Fork 拆分,第二步进行 Join 合并,我们先来看一下它的类图结构
ForkJoinPool 的使用也是通过调用 submit(ForkJoinTask<T> task) 或 invoke(ForkJoinTask<T> task) 方法来执行指定任务了。其中任务的类型是 ForkJoinTask 类,它代表的是一个可以合并的子任务,他本身是一个抽象类,同时还有两个常用的抽象子类 RecursiveAction 和 RecursiveTask ,其中 RecursiveTask 表示的是有返回值类型的任务,而 RecursiveAction 则表示无返回值的任务。下面是它们的类图:
下面我们通过一个简单的代码先来看一下如何使用 ForkJoinPool 线程池
/** * @url: i-code.online * @author: AnonyStar * @time: 2020/11/2 10:01 */ public class ForkJoinApp1 { /** 目标: 打印0-200以内的数字,进行分段每个间隔为10以上,测试forkjoin */ public static void main(String[] args) { // 创建线程池, ForkJoinPool joinPool = new ForkJoinPool(); // 创建根任务 SubTask subTask = new SubTask(0,200); // 提交任务 joinPool.submit(subTask); //让线程阻塞等待所有任务完成 在进行关闭 try { joinPool.awaitTermination(2, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } joinPool.shutdown(); } } class SubTask extends RecursiveAction { int startNum; int endNum; public SubTask(int startNum,int endNum){ super(); this.startNum = startNum; this.endNum = endNum; } @Override protected void compute() { if (endNum - startNum < 10){ // 如果分裂的两者差值小于10 则不再继续,直接打印 System.out.println(Thread.currentThread().getName()+": [startNum:"+startNum+",endNum:"+endNum+"]"); }else { // 取中间值 int middle = (startNum + endNum) / 2; //创建两个子任务,以递归思想, SubTask subTask = new SubTask(startNum,middle); SubTask subTask1 = new SubTask(middle,endNum); //执行任务, fork() 表示异步的开始执行 subTask.fork(); subTask1.fork(); } } }结果:
从上面的案例我们可以看到我们,创建了很多个线程执行,因为我测试的电脑是12线程的,所以这里实际是创建了12个线程,也侧面说明了充分调用了每个处理的线程处理能力
上面案例其实我们发现很熟悉的味道,那就是以前接触过的递归思想,将上面的案例图像化如下,更直观的看到,