java中常见的六种线程池详解( 三 )

scheduleWithFixedDelay 是以任务执行结束的时间点作为计时的开始 。如下所示

java中常见的六种线程池详解

文章插图
SingleThreadScheduledExecutor
  • 它实际和 ScheduledThreadPool 线程池非常相似,它只是 ScheduledThreadPool的一个特例,内部只有一个线程,它只是将 ScheduledThreadPool 的核心线程数设置为了 1 。如源码所示:
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {return new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1));}
  • 上面我们介绍了五种常见的线程池,对于这些线程池我们可以从核心线程数、最大线程数、存活时间三个维度进行一个简单的对比,有利于我们加深对这几种线程池的记忆 。
 FixedThreadPoolSingleThreadExecutorCachedThreadPoolScheduledThreadPoolSingleThreadScheduledExecutor corePoolSize构造函数传入10构造函数传入1maxPoolSize同corePoolSize1Integer. MAX_VALUEInteger. MAX_VALUEInteger. MAX_VALUEkeepAliveTime006000 ForkJoinPool
  • ForkJoinPool 这是一个在 JDK7 引入的新新线程池,它的主要特点是可以充分利用多核CPU , 可以把一个任务拆分为多个子任务,这些子任务放在不同的处理器上并行执行,当这些子任务执行结束后再把这些结果合并起来,这是一种分治思想 。
  • ForkJoinPool 也正如它的名字一样,第一步进行 Fork 拆分,第二步进行 Join 合并,我们先来看一下它的类图结构

java中常见的六种线程池详解

文章插图
  • ForkJoinPool 的使用也是通过调用 submit(ForkJoinTask<T> task)或 invoke(ForkJoinTask<T> task) 方法来执行指定任务了 。其中任务的类型是 ForkJoinTask 类,它代表的是一个可以合并的子任务,他本身是一个抽象类,同时还有两个常用的抽象子类 RecursiveAction 和 RecursiveTask ,其中 RecursiveTask 表示的是有返回值类型的任务,而 RecursiveAction 则表示无返回值的任务 。下面是它们的类图:

java中常见的六种线程池详解

文章插图
  • 下面我们通过一个简单的代码先来看一下如何使用 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();}}classSubTask extends RecursiveAction {int startNum;int endNum;public SubTask(int startNum,int endNum){super();this.startNum = startNum;this.endNum = endNum;}@Overrideprotected 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();}}}结果:
java中常见的六种线程池详解

文章插图
  • 从上面的案例我们可以看到我们,创建了很多个线程执行,因为我测试的电脑是12线程的,所以这里实际是创建了12个线程,也侧面说明了充分调用了每个处理的线程处理能力
  • 上面案例其实我们发现很熟悉的味道,那就是以前接触过的递归思想,将上面的案例图像化如下,更直观的看到,
【java中常见的六种线程池详解】
java中常见的六种线程池详解

文章插图