【线程池】Executors框架创建线程池

news/2024/11/8 0:46:21/

目录

1、newCachedThreadPool

2、newFixedThreadPool(fixed:固定的)

3、newSingleThreadExecutor

4、newScheduledThreadPool

5、newSingleThreadScheduledExecutor

6、newWorkStealingPool

7、为什么不推荐使用内置线程池?


以下是使用 Executors框架去创建的线程池,是Java内置的线程池,已经设好了相应的参数。

用法:ExecutorService executorService = Executors.newFixedThreadPool(2);

通过Executor框架对线程池的构建,都是基于 ThreadpoolExecutor 构造方法来构建的。

1newCachedThreadPool

作用:创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们,并在需要时使用提供的 ThreadFactory 创建新线程。整个线程池的线程数量并不固定,会随着需要来进行调整变化。该线程池使用同步队列 SynchronousQueue,该队列没有容量。

特征: 

(1)线程池中数量没有核心线程,并且非核心线程数量不固定,没有上限,可达到最大值(Interger. MAX_VALUE)。

(2)线程池中的线程可进行缓存重复利用和回收(回收默认时间为1分钟,线程空闲时间超过60秒就会被回收) 

(3)当线程池中,没有可用线程,会重新创建一个线程(会维持至少有一个空闲线程在线程池中)

(4)使用的是SynchronousQueue队列,只能存储一个任务

(5)60秒后将空闲线程关闭后,当线程池内线程为0时会自动关闭线程池

(6)初始化线程大小为0

创建方式: 

(1)Executors.newCachedThreadPool();

(2)Executors.newCachedThreadPool(ThreadFactory threadFactory);

源码:

public static ExecutorService newCachedThreadPool() {/** 0:表示核心线程数为0* Integer.MAX_VALUE:表示最大线程数为Integer.MAX_VALUE* 60L:表示线程空闲时间为60秒,当线程数量超过核心线程数时,超过的空闲线程如果空闲时间超过60秒就会被回收,因为这个线程池的核心线程数量设置的为0,所以这个线程池中所有的线程只要是空闲时间超过了60s,都会被回收* TimeUnit.SECONDS:表示秒为时间单位* new SynchronousQueue<Runnable>():表示线程池中使用的阻塞队列为同步队列 */return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {/** 0:表示核心线程数为0* Integer.MAX_VALUE:表示最大线程数为Integer.MAX_VALUE* 60L:表示线程空闲时间为60秒* TimeUnit.SECONDS:表示秒为时间单位* new SynchronousQueue<Runnable>():表示线程池中使用的阻塞队列为同步队列* threadFactory:表示线程工厂*/return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(),threadFactory);
}

执行流程:

1. 没有核心线程,直接向 SynchronousQueue 中提交任务。SynchronousQueue内部没有容器,该队列不会保留任务,任务达到后直接创建线程。

2. 如果有空闲线程,就去取出任务执行;如果没有空闲线程,就新建一个

3. 执行完任务的线程有 60 秒生存时间,如果在这个时间内可以接到新任务,就可以继续活下去,否则就被回收

适用场景:

  • 只适合短暂的任务,如果是非常耗时的任务不建议使用,因为每次提交一个任务都会开启一个线程,线程过多可能导致OMM
  • 用来创建一个可以无限扩大的线程池,适用于服务器负载较轻,执行很多短期异步任务。

2newFixedThreadPoolfixed:固定的)

作用:创建一个可重用固定线程数的线程池,以无界队列方式来运行这些线程,该线程池的线程数在创建时就被固定了,不可以扩大。在任意时间点,在大多数线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在线程关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在,即使线程一直处在空闲状态且队列为空,也不会被回收,直到调用shutdown。该线程池使用无界队列 LinkedBlockingQueue,该队列的最大容量为 Integer.MAX_VALUE,可以认为是无界的。因为不可以扩大线程数,所以也就不存在回收线程。

特征: 

(1)FixedThreadPool 的核心线程数和最大线程数都是指定值,也就是说当线程池中的线程数超过核心线程数后,任务都会被放到阻塞队列中。该线程池的核心线程数量和最大线程数量是一样的,即全部都为核心线程。

(2)FixedThreadPool选用的阻塞队列是 LinkedBlockingQueue,使用的是默认容量 Integer.MAX_VALUE, 相当于没有上限

(3)线程池中的线程处于一定的量,可以很好的控制线程的并发量 

(4)线程可以重复被使用,在线程被显示关闭之前,都将一直存在 

(5)初始化时该线程池中的线程数就定了,是固定的,不可以扩大

(6)当线程处于空闲,且任务队列为空时,线程池也不会关闭

创建方式: 

(1)Executors.newFixedThreadPool(int nThreads);//nThreads为线程的数量 

(2)Executors.newFixedThreadPool(int nThreads,ThreadFactory threadFactory);//nThreads为线程的数量,threadFactory创建线程的工厂方式

源码:

// 该线程池的最大线程数量和核心线程数量是一样的
public static ExecutorService newFixedThreadPool(int nThreads) {/*** nThreads:表示线程池中核心线程数量* nThreads:表示线程池中最大线程数量* 0L:表示线程池中空闲线程的存活时间,0表示当线程数量超过线程池的核心线程数后,超过的线程如果空闲时间超过0就会被回收,这也就说明该线程池的线程数量是不会超过nThreads的,超过的空闲线程就会被回收。但是如果没有超过核心线程数量,空闲线程就可以一直存活,除非显式地关闭线程* TimeUnit.MILLISECONDS:表示空闲线程的存活时间的单位* new LinkedBlockingQueue<Runnable>():表示线程池中的任务队列使用的是无界队列LinkedBlockingQueue*/return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}// 默认情况下该线程池地最大线程数量和核心线程数量都是1,说明该线程池中只有一个线程在工作
public static ExecutorService newSingleThreadExecutor() {/*** 1:表示线程池中核心线程数量* 1:表示线程池中最大线程数量* 0L:表示线程池中空闲线程的存活时间,0表示当线程数量超过线程池的核心线程数后,超过的线程如果空闲时间超过0就会被回收,这也就说明该线程池的线程数量是不会超过nThreads的,超过的空闲线程就会被回收。但是如果没有超过核心线程数量,空闲线程就可以一直存活,除非显式地关闭线程* TimeUnit.MILLISECONDS:表示空闲线程的存活时间的单位* new LinkedBlockingQueue<Runnable>():表示线程池中的任务队列使用的是无界队列LinkedBlockingQueue*/return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}

执行流程:

1. 线程数少于核心线程数,也就是设置的线程数时,新建线程执行任务

2. 线程数等于核心线程数后,将任务加入阻塞队列

3. 由于队列容量非常大,可以一直添加

4. 执行完任务的线程反复去队列中取任务执行

适用场景:

主要适用于固定大小的线程池,因为它是无界的阻塞队列,那么线程池中的线程不会扩大,适用与可以预测线程数的场景中,或者服务器的负载很高,为了资源的合理利用,需要对线程数量进行严格控制的场景中。

3newSingleThreadExecutor

作用:创建一个使用单个 worker 线程的 线程池,即该线程池只会有一个线程处于活动状态,所以任务只能一个一个被执行。以无界队列方式来运行该线程。(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,一个新线程将代替它执行后续的任务)。可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。该线程池使用无界队列 LinkedBlockingQueue,该队列的最大容量为 Integer.MAX_VALUE,可以认为是无界的。

特征: 

(1)线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行

(2)该线程池是一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行

(3)SingleThreadExecutor选用的阻塞队列是 LinkedBlockingQueue,使用的是默认容量 Integer.MAX_VALUE, 相当于没有上限

(4)newSingleThreadExecutor返回的是一个经过代理的ExecutorService,不能转换为ThreadPoolExecutor,这也就意味着它只有一些ExecutorService的基本方法

(5)SingleThreadExecutor与单独new出来的Thread区别在于,单独new出来的Thread任务结束之后线程也就会随着结束,而且不可以submit提交任务到队列

创建方式: 

(1)Executors.newSingleThreadExecutor() ; 

(2)Executors.newSingleThreadExecutor(ThreadFactory threadFactory);// threadFactory创建线程的工厂方式

源码:

public static ExecutorService newSingleThreadExecutor() {/*** 1(corePoolSize):表示线程池中的核心线程数量* 1:表示线程池的最大线程数量* 0L(keepAliveTime):表示线程池中超过corePoolSize数目的空闲线程最大存活时间,该线程池是值得核心线程数量为1,所以只要是线程池中的线程数量超过1,该空闲线程就会被回收* TimeUnit.MILLISECONDS:参数keepAliveTime的时间单位* new LinkedBlockingQueue<Runnable>():一个阻塞队列,用来存储等待执行的任务*/return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {/*** 1(corePoolSize):表示线程池中的核心线程数量* 1:表示线程池的最大线程数量* 0L(keepAliveTime):表示线程池中超过corePoolSize数目的空闲线程最大存活时间,该线程池是值得核心线程数量为1,所以只要是线程池中的线程数量超过1,该空闲线程就会被回收* TimeUnit.MILLISECONDS:参数keepAliveTime的时间单位* new LinkedBlockingQueue<Runnable>():一个阻塞队列,用来存储等待执行的任务* threadFactory:线程工厂,用来创建线程*/return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory));
}

适用场景:

适用于需要保证任务被按顺序执行,并且在任何时候都不会出现多个线程的情况。

4newScheduledThreadPool

作用: 创建一个线程池,它可以实现在给定延迟后运行命令或者定期地执行。该线程池使用延迟阻塞队列 DelayedWorkQueue

特征: 

(1)线程池中具有指定数量的线程,即便是空线程也将保留 

(2)可定时或者延迟执行线程活动

创建方式: 

(1)Executors.newScheduledThreadPool(int corePoolSize);// corePoolSize线程的个数 

(2)newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory);// corePoolSize线程的个数,threadFactory创建线程的工厂

源码:

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {// corePoolSize:表示线程池中的核心线程数return new ScheduledThreadPoolExecutor(corePoolSize);
}public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) {// corePoolSize:表示线程池中的核心线程数// threadFactory:线程工厂,用于创建线程return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}

5newSingleThreadScheduledExecutor

作用: 创建一个单线程执行程序,它可实现在给定延迟后运行命令或者定期地执行。该线程池使用延迟阻塞队列 DelayedWorkQueue

特征: 

(1)线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行 

(2)可定时或者延迟执行线程活动

创建方式: 

(1)Executors.newSingleThreadScheduledExecutor() ; 

(2)Executors.newSingleThreadScheduledExecutor(ThreadFactory threadFactory) ;//threadFactory创建线程的工厂

源码:

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {return new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1));
}public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {return new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1, threadFactory));
}

6newWorkStealingPool

特征:

(1)该方法会根据你的CPU核数来创建线程个数,也可指定线程数

(2)newWorkStealingPool其内部使用的是ForkJoinPool,在任务全部执行完之后该线程池会自动关闭

适用场景:

创建一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行,适用于大耗时的操作,可以并行来执行

7为什么不推荐使用内置线程池?

在《阿里巴巴 Java 开发手册》“并发处理”这一章节,明确指出线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。

为什么呢?

  • 使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源开销,解决资源不足的问题。如果不使用线程池,有可能会造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

另外,《阿里巴巴 Java 开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 构造函数的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

Executors 返回线程池对象的弊端如下(后文会详细介绍到):

  • FixedThreadPool 和 SingleThreadExecutor:使用的是无界的 LinkedBlockingQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。
  • CachedThreadPool:使用的是同步队列 SynchronousQueue, 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。
  • ScheduledThreadPool 和 SingleThreadScheduledExecutor : 使用的无界的延迟阻塞队列DelayedWorkQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。

所以不建议用下面这些方法创建线程池,还是直接使用ThreadPoolExecutor的构造方法,自己制定相应的参数来创建。


相关文章:【线程池】Java的线程池
                  【线程池】Java线程池的核心参数


http://www.ppmy.cn/news/99879.html

相关文章

分布式网络通信框架(十)——Mprpc框架使用示例

发布一个服务提供远程调用方法的流程 若想要发布一个服务提供一些远程调用方法&#xff0c;步骤如下&#xff1a; 先在protobuf文件中添加参数和返回值的message 类型&#xff0c;然后再添加希望提供的服务 service 类型&#xff08;如UserServiceRpc&#xff09;和 其中的方…

看完这篇Markdown你就会了

Markdown是一种轻量级标记语言&#xff0c;它的设计目标是让人们使用简单的语法快速地书写文本内容&#xff0c;并且可以转换为HTML等其他格式。Markdown的语法非常简单易学&#xff0c;只需要掌握几个基本的符号即可&#xff0c;相比于HTML等其他标记语言&#xff0c;Markdown…

如何正确地使用ES6提高我们的代码质量

前言 相信每个前端工程师&#xff0c;或者了解前端的人都知道ES6。它是js的一次巨变&#xff0c;它为我们开发js前端项目的时候带来了许多更好的去书写代码的方式。但是很多时候我们可能都没有过度地去关注优化代码这一块内容&#xff0c;哪怕有也只是注意到了一些比较大众化&…

K210入门-环境搭建与点灯测试(一)

目录 1、简介 2、资质查找 3、IDE下载安装 4、测试程序 4.1 测序复制 4.2 开发板选择 4.3 链接 4.4 效果展示 1、简介 本文主要针对小白使用K210进行入门&#xff0c;以及自己学习的总结与笔记使用。本文主要进行环境搭建与点灯测试。 2、资质查找 首先去官网进行资料下…

深度学习基本功3:NMS(Non-Maximum Suppression,非极大值抑制)算法原理及实现

文章目录 1. 为什么要使用NMS2. NMS算法原理2.1 IoU与置信度2.2 算法流程 3. Python代码实现 1. 为什么要使用NMS 大多数目标检测算法&#xff08;稠密预测&#xff09;在得到最终的预测结果时&#xff0c;特征图的每个位置都会输出多个检测结果&#xff0c;整个特征图上会出很…

14巧探细节:gRPC的UnknownService接口

gRPC UnknownServiceHandler是一个gRPC内置的一种拦截器,用于处理未知的服务请求。具体的使用案例可以是在服务端实现一个UnknownServiceHandler,当客户端请求一个不存在的服务时,服务端会返回一个自定义的错误信息,而不是默认的 gRPC 错误信息以提高服务的可读性。接下来让…

SPA首屏加载速度慢的怎么解决?

SPA首屏加载速度慢的怎么解决&#xff1f; 加载慢的原因 网络延时问题资源文件体积是否过大资源是否重复发送请求去加载了加载脚本的时候&#xff0c;渲染内容堵塞了 解决方案 1.减小入口文件体积 常用的手段是路由懒加载&#xff0c;把不同路由对应的组件分割成不同的代码…

上海城市开发者社区小聚有感

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是Rockey&#xff0c;不知名企业的不知名Java开发工程师 &#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&#x1f44d;一下博主哦 &#x1f4dd;联系方式&#xff1a;he18339193956&…