【多线程】ThreadPoolExecutor 类的使用详解
ThreadPoolExecutor 构造方法
ThreadPoolExecutor共4个构造方法:
咱们直接看参数最多的7个参数分别代表:
public ThreadPoolExecutor(// 线程池核心线程数int corePoolSize, // 线程池最大数int maximumPoolSize, // 空闲线程存活时间long keepAliveTime, // 时间单位TimeUnit unit,// 线程池所使用的缓冲队列BlockingQueue<Runnable> workQueue,// 线程池创建线程使用的工厂ThreadFactory threadFactory,// 线程池对拒绝任务的处理策略RejectedExecutionHandler handler)
一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。
一个任务通过execute(Runnable)方法欲添加到线程池时
- 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
- 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
- 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
- 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
处理任务的优先级为
- 核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
当线程池中的线程数量大于 corePoolSize 时,如果某线程空闲时间超过 keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
workQueue任务队列
任务队列,被添加到线程池中,但尚未被执行的任务;它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种;
直接切换
设置为SynchronousQueue队列,SynchronousQueue是一个特殊的BlockingQueue,它没有容量,没执行一个插入操作就会阻塞,需要再执行一个删除操作才会被唤醒,反之每一个删除操作也都要等待对应的插入操作。
使用SynchronousQueue队列,提交的任务不会被保存,总是会马上提交执行。如果用于执行任务的线程数量小于maximumPoolSize,则尝试创建新的进程,如果达到maximumPoolSize设置的最大值,则根据你设置的handler执行拒绝策略。因此这种方式你提交的任务不会被缓存起来,而是会被马上执行,在这种情况下,你需要对你程序的并发量有个准确的评估,才能设置合适的maximumPoolSize数量,否则很容易就会执行拒绝策略
无界队列
一般使用基于链表的阻塞队列LinkedBlockingQueue。使用无界任务队列,线程池的任务队列可以无限制的添加新的任务,而线程池创建的最大线程数量就是你corePoolSize设置的数量,也就是说在这种情况下maximumPoolSize这个参数是无效的,哪怕你的任务队列中缓存了很多未执行的任务,当线程池的线程数达到corePoolSize后,就不会再增加了;若后续有新的任务加入,则直接进入队列等待,当使用这种任务队列模式时,一定要注意你任务提交与处理之间的协调与控制,不然会出现队列中的任务由于无法及时处理导致一直增长,直到最后资源耗尽的问题。
使用有界队列
一般使用ArrayBlockingQueue。使用该方式可以将线程池的最大线程数量限制为maximumPoolSize,这样能够降低资源的消耗,但同时这种方式也使得线程池对线程的调度变得更困难,因为线程池和队列的容量都是有限的值,所以要想使线程池处理任务的吞吐率达到一个相对合理的范围,又想使线程调度相对简单,并且还要尽可能的降低线程池对资源的消耗,就需要合理的设置这两个数量。
使用ArrayBlockingQueue有界任务队列,若有新的任务需要执行时,线程池会创建新的线程,直到创建的线程数量达到corePoolSize时,则会将新的任务加入到等待队列中。若等待队列已满,即超过ArrayBlockingQueue初始化的容量,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于maximumPoolSize,则执行拒绝策略。在这种情况下,线程数量的上限与有界任务队列的状态有直接关系,如果有界队列初始容量较大或者没有达到超负荷的状态,线程数将一直维持在corePoolSize以下,反之当任务队列已满时,则会以maximumPoolSize为最大线程数上限。
- 如果要想降低系统资源的消耗(包括CPU的使用率,操作系统资源的消耗,上下文环境切换的开销等), 可以设置较大的队列容量和较小的线程池容量, 但这样也会降低线程处理任务的吞吐量。
- 如果提交的任务经常发生阻塞,那么可以考虑通过调用 setMaximumPoolSize() 方法来重新设定线程池的容量。
- 如果队列的容量设置的较小,通常需要将线程池的容量设置大一点,这样CPU的使用率会相对的高一些。但如果线程池的容量设置的过大,则在提交的任务数量太多的情况下,并发量会增加,那么线程之间的调度就是一个要考虑的问题,因为这样反而有可能降低处理任务的吞吐量。
ThreadPoolExecutor内部有实现4个拒绝策略,默认为AbortPolicy策略
- CallerRunsPolicy:由调用execute方法提交任务的线程来执行这个任务
- AbortPolicy:抛出异常RejectedExecutionException拒绝提交任务
- DiscardPolicy:直接抛弃任务,不做任何处理
- DiscardOldestPolicy:去除任务队列中的第一个任务,重新提交
ThreadPoolExecutor 使用
1,当池中正在运行的线程数(包括空闲线程数)小于corePoolSize时,新建线程执行任务
public static void main(String[] args) {ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1));// 任务1pool.execute(() -> {try {Thread.sleep(3 * 1000);System.out.println("--helloWorld_001--" + Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}});//任务2pool.execute(() -> System.out.println("--helloWorld_002--" + Thread.currentThread().getName()));}
运行结果:
–helloWorld_001–pool-1-thread-1
–helloWorld_002–pool-1-thread-2
结论:线程1 结束后 没有继续线程1 而是启动线程2
2,当池中正在运行的线程数(包括空闲线程数)大于等于corePoolSize时,新插入的任务进入workQueue排队(如果workQueue长度允许),等待空闲线程来执行。
public static void main(String[] args) {ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1));// 任务1pool.execute(() -> {try {Thread.sleep(3 * 1000);System.out.println("--helloWorld_001--" + Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}});// 任务2pool.execute(() -> {try {Thread.sleep(5 * 1000);System.out.println("--helloWorld_002--" + Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}});// 任务3pool.execute(() -> System.out.println("--helloWorld_003--" + Thread.currentThread().getName()));}
运行结果:
–helloWorld_001–pool-1-thread-1
–helloWorld_003–pool-1-thread-1
–helloWorld_002–pool-1-thread-2
结论:任务2在运行过程中,任务3启动不会新建线程,因为有一个队列是空的,maximumPoolSize=3这个参数不起作用。
3,当队列里的任务达到上限,并且池中正在进行的线程小于maxinumPoolSize,对于新加入的任务,新建线程。
public static void main(String[] args) {ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1));// 任务1pool.execute(() -> {try {Thread.sleep(3 * 1000);System.out.println("--helloWorld_001--" + Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}});// 任务2pool.execute(() -> {try {Thread.sleep(5 * 1000);System.out.println("--helloWorld_002--" + Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}});// 任务3pool.execute(() -> System.out.println("--helloWorld_003--" + Thread.currentThread().getName()));// 任务4pool.execute(() -> System.out.println("--helloWorld_004--" + Thread.currentThread().getName()));}
运行结果:
–helloWorld_004–pool-1-thread-3
–helloWorld_003–pool-1-thread-3
–helloWorld_001–pool-1-thread-1
–helloWorld_002–pool-1-thread-2
结果:任务1,2启动后 任务3在队列 , 队列就满了, 由于正在进行的线程数是2 < maximumPoolSize,只能新建一个线程了 然后任务4就进了新线程-3,任务4结束,队列里的任务3在线程3 进行。
4,队列里的任务达到上限,并且池中正在运行的线程等于maximumPoolSize,对于新加入的任务,执行拒绝策略(线程池默认的策略是抛异常)。
public static void main(String[] args) {ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1));// 任务1pool.execute(() -> {try {Thread.sleep(3 * 1000);System.out.println("--helloWorld_001--" + Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}});// 任务2pool.execute(() -> {try {Thread.sleep(5 * 1000);System.out.println("--helloWorld_002--" + Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}});// 任务3pool.execute(() -> System.out.println("--helloWorld_003--" + Thread.currentThread().getName()));// 任务4pool.execute(() -> {try {Thread.sleep(2 * 1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("--helloWorld_004--" + Thread.currentThread().getName());});// 任务5pool.execute(() -> System.out.println("--helloWorld_005--" + Thread.currentThread().getName()));}
运行结果:
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task cs.wy.Thread.ThreadPoolExecutorTest$$Lambda$5/999966131@7699a589 rejected from java.util.concurrent.ThreadPoolExecutor@58372a00[Running, pool size = 3, active threads = 3, queued tasks = 1, completed tasks = 0]at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)at cs.wy.Thread.ThreadPoolExecutorTest.main(ThreadPoolExecutorTest.java:40)
--helloWorld_004--pool-1-thread-3
--helloWorld_003--pool-1-thread-3
--helloWorld_001--pool-1-thread-1
--helloWorld_002--pool-1-thread-2
结论:队列达到上限,线程池达到最大值,故抛出异常。
关闭线程
分为两种方式:
//平缓关闭,不允许新的线程加入,正在运行的都跑完即可关闭。
pool.shutdown();
//暴力关闭。不允许新的线程加入,且直接停到正在进行的线程。
pool.shutdownNow();
【多线程】ThreadPoolExecutor 类的使用详解相关推荐
- java多线程学习-java.util.concurrent详解
http://janeky.iteye.com/category/124727 java多线程学习-java.util.concurrent详解(一) Latch/Barrier 博客分类: java ...
- java多线程中的join方法详解
java多线程中的join方法详解 方法Join是干啥用的? 简单回答,同步,如何同步? 怎么实现的? 下面将逐个回答. 自从接触Java多线程,一直对Join理解不了.JDK是这样说的:join p ...
- java 重启线程_java 可重启线程及线程池类的设计(详解)
了解JAVA多线程编程的人都知道,要产生一个线程有两种方法,一是类直接继承Thread类并实现其run()方法:二是类实现Runnable接口并实现其run()方法,然后新建一个以该类为构造方法参数的 ...
- python多线程读取数据库数据_Python基于多线程操作数据库相关知识点详解
Python基于多线程操作数据库相关问题分析 本文实例分析了Python多线程操作数据库相关问题.分享给大家供大家参考,具体如下: python多线程并发操作数据库,会存在链接数据库超时.数据库连接丢 ...
- Android之TelephonyManager类的方法详解
林计钦-JAVA java技术群:127834248 博客园 首页 社区 新随笔 联系 订阅 管理 随笔-105 评论-24 文章-0 trackbacks-0 Andr ...
- 对python3中pathlib库的Path类的使用详解
原文连接 https://www.jb51.net/article/148789.htm 1.调用库 ? 1 from pathlib import 2.创建Path对象 ? 1 2 3 4 5 ...
- c++类的构造函数详解
c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初 ...
- python3库_对python3中pathlib库的Path类的使用详解
用了很久的os.path,今天发现竟然还有这么好用的库,记录下来以便使用. 1.调用库 from pathlib import 2.创建Path对象 p = Path('D:/python/1.py' ...
- UML类图与类的关系详解
UML类图与类的关系详解 2011-04-21 来源:网络 在画类图的时候,理清类和类之间的关系是重点.类的关系有泛化(Generalization).实现(Realization).依赖(D ...
最新文章
- ubuntu下不同版本python安装pip及pip的使用
- vi文本编辑器的使用
- linux在所有文件中查找某一个字符
- 一本通1629聪明的燕姿
- Django学习知识点、路线图、资料总结大全,建议收藏!
- 免费的Andr​​oid最好的视频播放器应用程序2012
- 二进制颜色查询对照表
- PHP字符串函数ucfirst( 将字符串的首字母转换为大写)
- python基础总结:1.6、流程控制
- LPC1768/1769之CAN控制器概述(附库函数下载地址)
- 深入浅出学算法008-韩信点兵
- adobe acrobat 无效批注对象
- 一次性下载CVPR/ICCV/ECCV会议所有论文并提取论文标题重命名pdf文件
- 二手苹果手机价格表最新
- Web入门(1)——制作简单的网页
- 一下搞懂HTTP协议
- 学日语、记单词是有规律的
- 使用zxing生成二维码的方法步骤
- java mdb文件_java读取mdb文件
- (未完成)历届国赛题目分析(2006)