线程池:4个方法,7个参数,4种拒绝策略
什么是池?
先讲一个例子,有可能可以帮助你理解,觉得无趣的小伙伴可以直接跳过。相信大多数都知道外包公司,甚至很多小伙伴还在外包公司呆过,其实外包公司我觉得也就可以看作是个"池"。
比如说小明最近离开了外包公司,自己带着几个兄弟组建了一个开发小组成立了个工作室并开始接项目,他们也开始了没日没夜的撸码干活,开足了马力同时干着三四个项目,忙的个不亦乐乎。
可是随着他们接的项目越来越多了,小明和小伙伴们熬夜熬的开始掉头发了,这样下去说不定哪天就栽倒在电脑桌下再也不能撸码了。于是他们一商量开了个外包公司A由小明负责,然后组建5个Java开发小组,不久后公司就接到了来自华为的一个外包项目需要3个开发小组,剩下两个小组待命,接着又接了一个阿里的外包项目需要3个开发小组,可是人员不够,本着向钱看,向厚看的人生信仰,小明赶紧招兵买马又扩充了一个开发小组,所有人都撸起袖子加油干,小明笑得很灿烂,数钱数到手抽筋。 过了段时间又接了个公司的项目,可是小明一看工期有点短,要是再扩招一个开发组那干完了接不上岂不是要白白养这么多人吗,一拍脑袋他想到个妙招,组建了一支兼职的队伍做完了就可以解散,要是又有短期的项目又可以把他们召集起来,小明开始在办公室悠闲地喝着茶,打着吃鸡。 过了几天来了个小公司的项目,钱少事多,小明翻了翻白眼直接给拒绝了,继续喝茶,吃鸡。。。
好了,这只是我在写总结的时候突然脑袋里冒出的一个虚拟故事,开始进入正题。
一、哪4个方法?
JDK8有新增方法newWorkStealingPool,在此不作讨论。
// 创建单个线程
Executors.newSingleThreadExecutor();
// 创建固定数量的线程
Executors.newFixedThreadPool(3);
// 可动态调整,随着请求的增多线程也随之创建
Executors.newCachedThreadPool();
// 用来调度即将执行的任务的线程池
Executors.newScheduledThreadPool();
newSingleThreadExecutor
无论调用多少次都是同一个线程,就像小明和小伙伴刚成立工作室,无论他们接了多少个项目都只有他们一个组没日没夜的干。
@Testpublic void testNewSingleThreadExecutor() {ExecutorService executorService = Executors.newSingleThreadExecutor();try {for (int i = 1; i <= 10; i++) {executorService.execute(() -> System.out.println("当前线程=>" + Thread.currentThread().getName()));}} catch (Exception e) {e.printStackTrace();} finally {executorService.shutdown();}}
打印出来显示只产生了一个线程。
当前线程=>pool-1-thread-1
当前线程=>pool-1-thread-1
当前线程=>pool-1-thread-1
当前线程=>pool-1-thread-1
当前线程=>pool-1-thread-1
当前线程=>pool-1-thread-1
当前线程=>pool-1-thread-1
当前线程=>pool-1-thread-1
当前线程=>pool-1-thread-1
当前线程=>pool-1-thread-1
newFixedThreadPool
固定地创建5个线程,当小明成立外包公司后有5个开发组,需要3个组的时候就调用出去3个组,如果需要6个那其中至少有一个组就必需多做一个项目。
@Testpublic void testNewFixedThreadPool() {ExecutorService executorService = Executors.newFixedThreadPool(5);try {for (int i = 1; i <= 6; i++) {executorService.execute(() -> System.out.println("当前线程=>" + Thread.currentThread().getName()));}} catch (Exception e) {e.printStackTrace();} finally {executorService.shutdown();}}
线程pool-1-thread-1被打印了两次,当然每次执行的结果可能都是不同的,但其中有一个线程肯定会打印两次。
当前线程=>pool-1-thread-1
当前线程=>pool-1-thread-1
当前线程=>pool-1-thread-2
当前线程=>pool-1-thread-3
当前线程=>pool-1-thread-4
当前线程=>pool-1-thread-5
newCachedThreadPool
创建可伸缩的线程,好比小明组建的兼职开发,可以根据项目情况再组建两个兼职开发组。
@Testpublic void testNewCachedThreadPool() {ExecutorService executorService = Executors.newCachedThreadPool();try {for (int i = 1; i <= 10; i++) {executorService.execute(() -> System.out.println("当前线程=>" + Thread.currentThread().getName()));}} catch (Exception e) {e.printStackTrace();} finally {executorService.shutdown();}}
每次执行的打印结果都可能不同,可能是6个线程,也可能是7个。。。
当前线程=>pool-1-thread-1
当前线程=>pool-1-thread-2
当前线程=>pool-1-thread-3
当前线程=>pool-1-thread-4
当前线程=>pool-1-thread-5
当前线程=>pool-1-thread-6
当前线程=>pool-1-thread-3
当前线程=>pool-1-thread-5
当前线程=>pool-1-thread-6
当前线程=>pool-1-thread-4
newScheduledThreadPool
定时调度。
newScheduledThreadPool共计有三个方法:
schedule(commod,delay,unit) 系统启动后,需要等待多久执行,delay是等待时间。只执行一次,没有周期性。
scheduleAtFixedRate(commod,initialDelay,period,unit) 以period为固定周期时间,按照一定频率来重复执行任务,initialDelay是说系统启动后,需要等待多久才开始执行。例如:设置了period为5秒,线程启动之后执行大于5秒,线程结束之后,立即启动线程的下一次,如果线程启动之后只执行了3秒就结束了,那执行下一次需要等待2秒再执行。这个是优先保证任务执行的频率。
scheduleWithFixedDelay(commod,initialDelay,delay,unit) 以delay为固定延迟时间,按照一定的等待时间来执行任务,initialDelay意义与上面的相同。例如:设置了delay为5秒,线程启动之后不管执行了多久,结束之后都需要先过5秒,才能执行下一次。这个是优先保证任务执行的间隔。
以scheduleWithFixedDelay为例。
@Testpublic void testScheduledThreadPool() {ScheduledExecutorService scheduler = null;try {scheduler = Executors.newScheduledThreadPool(1);ScheduledFuture<?> scheduleTask =scheduler.scheduleWithFixedDelay(() -> System.out.println("run()"),5,1,TimeUnit.SECONDS);} catch (Exception e) {e.printStackTrace();} finally {// 一般池化技术都需要关闭资源scheduler.shutdown();}}
建议
以上四种方法其实在工作中都基本使用不到,当然阿里的Java开发手册中强制规定不允许使用Executors方式来创建,而建议使用ThreadPoolExecutor的方式。(阿里巴巴Java开发手册2020)
二、又是哪7个参数?
ThreadPoolExecutor
我们分别点进各个方法的源码里面可以发现都是调用了ThreadPoolExecutor这个类来创建(也就是上面阿里强制使用的方法)。
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}
newScheduledThreadPool是先调用了ScheduledThreadPoolExecutor,这个也是继承ScheduledThreadPoolExecutor,所以调用super也就还是用的ThreadPoolExecutor。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {return new ScheduledThreadPoolExecutor(corePoolSize);}public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());}public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);}
再去看ThreadPoolExecutor这个构造方法,这里的参数就是所说的的七个参数。
/*** Creates a new {@code ThreadPoolExecutor} with the given initial* parameters.** @param corePoolSize the number of threads to keep in the pool, even* if they are idle, unless {@code allowCoreThreadTimeOut} is set* @param maximumPoolSize the maximum number of threads to allow in the* pool* @param keepAliveTime when the number of threads is greater than* the core, this is the maximum time that excess idle threads* will wait for new tasks before terminating.* @param unit the time unit for the {@code keepAliveTime} argument* @param workQueue the queue to use for holding tasks before they are* executed. This queue will hold only the {@code Runnable}* tasks submitted by the {@code execute} method.* @param threadFactory the factory to use when the executor* creates a new thread* @param handler the handler to use when execution is blocked* because the thread bounds and queue capacities are reached* @throws IllegalArgumentException if one of the following holds:<br>* {@code corePoolSize < 0}<br>* {@code keepAliveTime < 0}<br>* {@code maximumPoolSize <= 0}<br>* {@code maximumPoolSize < corePoolSize}* @throws NullPointerException if {@code workQueue}* or {@code threadFactory} or {@code handler} is null*/public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.acc = System.getSecurityManager() == null ?null :AccessController.getContext();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;}
corePoolSize 核心线程数,一直存活,即使线程数小于核心线程数且线程数有空闲,线程池也会创建新的线程。
maximumPoolSize 最大线程数,当线程数大于核心线程数并且任务队列已经满了的时候,线程池会创建新的线程,当线程数大于最大线程数并且任务队列已经满了,会抛出异常。
keepAliveTime 线程空闲时间,当线程的空闲时间达到keepAliveTime时,线程会退出,直到线程数等于核心线程数,可以设置参数allowCoreThreadTimeout=true,则会直到线程数为0。
TimeUnit unit 超时时间单位。
BlockingQueue workQueue 阻塞队列,任务队列的容量。
ThreadFactory threadFactory 线程工厂,基本不用设置(默认使用Executors.defaultThreadFactory())
RejectedExecutionHandler handler 拒绝策略,任务拒绝处理器。
三、哪4种拒绝策略?
可以在源码中看到默认用的是AbortPolicy。
/*** The default rejected execution handler*/private static final RejectedExecutionHandler defaultHandler =new AbortPolicy();
四种分为是:
AbortPolicy() 线程池满了,如果还有线程想加入,不处理这个请求,抛出异常。
CallerRunsPolicy() 哪来的回哪去
DiscardPolicy() 队列满了,丢掉任务,不会抛出异常。
DiscardOldestPolicy() 队列满了,尝试去和最早的竞争,不会抛出异常。
总结
其实线程池就是种池化技术,其他的比如还有数据库连接池,内存池,http连接池等等,这样可以减少资源对象的创建次数,垃圾回收的开销,提高程序的性能,特别是在高并发下这种提高更加明显。
线程池:4个方法,7个参数,4种拒绝策略相关推荐
- 线程池ThreadPool,线程池底层ThreadPoolExecutor方法七大参数,拒绝策略,以及实际开发中高并发下用到哪个线程池?
为什么要用线程池 基本的三个线程池的底层就是ThreadPoolExecutor类 ExecutorService threadPool = Executors.newFixedThreadPool( ...
- 线程池三大方法,七大参数,四种拒绝策略
线程和进程: 进程: 一个程序,是执行程序的一次执行过程. 一个进程往往包含若干个线程,线程是cpu调度和执行的单位. Java默认有2个线程:main.GC 池化技术: 01:程序的运行,本质 :占 ...
- 为什么线程池里的方法会执行两次_新手一看就懂的线程池
作者:码农田小齐 来源:https://www.cnblogs.com/nycsde/p/14003888.html 那相信大家也能感受到,其实用多线程是很麻烦的,包括线程的创建.销毁和调度等等,而且 ...
- java中线程池的使用方法
1 引入线程池的原因 由于线程的生命周期中包括创建.就绪.运行.阻塞.销毁阶段,当我们待处理的任务数目较小时,我们可以自己创建几个线程来处理相应的任务,但当有大量的任务时,由于创建.销毁线程需要很大的 ...
- 深入分析3种线程池执行任务的逻辑方法
摘要:结合ThreadPoolExecutor类的源码深度分析线程池执行任务的整体流程. 本文分享自华为云社区<[高并发]通过ThreadPoolExecutor类的源码深度解析线程池执行任务的 ...
- 线程池的执行原则及配置参数详解
池是一种非常优秀的设计思想,通过建立池可以有效的利用系统资源,节约系统性能.Java 中的线程池就是一种非常好的实现,从 JDK 1.5 开始 Java 提供了一个线程工厂 Executors 用来生 ...
- 为什么线程池里的方法会执行两次_别以为线程池很简单,来回答下这些问题!...
前言 线程池可以说是 Java 进阶必备的知识点了,也是面试中必备的考点,可能不少人看了这篇文章后能对线程池工作原理说上一二,但这还远远不够,如果碰到比较有经验的面试官再继续追问,很可能会被吊打,考虑 ...
- 击穿线程池面试题:3大方法,7大参数,4种拒绝策略
前言:多线程知识是Java面试中必考的点.本文详细介绍--线程池.在实际开发过程里,很多IT从业者使用率不高,也只是了解个理论知识,和背诵各种八股文,没有深入理解到脑海里,导致面试完就忘.--码农 = ...
- Java Executor源码解析(3)—ThreadPoolExecutor线程池execute核心方法源码【一万字】
基于JDK1.8详细介绍了ThreadPoolExecutor线程池的execute方法源码! 上一篇文章中,我们介绍了:Java Executor源码解析(2)-ThreadPoolExecutor ...
最新文章
- 梯度下降优化算法综述与PyTorch实现源码剖析
- 多媒体-设备的名称已被此应用程序用作别名,请使用唯一的别名
- 使用Filter跟踪Asp.net MVC页面加载(转)
- 《Shell脚本学习指南》第四章 文本处理工具
- linux实现命令解释器_想在Win10上安装Linux,只需一个命令即可实现
- asp.net站点时间格式与系统时间格式不一致。手动修改
- 拉美光伏新兴市场热潮将至
- 随机森林-集成学习方法(分类)
- Linux使用/proc/stat计算CPU使用率
- 宇枫资本投资过程中要注意这些习惯
- 如何设置和解除PDF文件保护?
- Pytorch3D_上手学习3D的AI模型
- excel 查找/替换 回车键
- MySQL-8.0 RESTART命令远程重启mysqld
- 县域远程医疗解决方案
- VScode断点调试出现unbound breakpoint(断点是灰色)问题
- 重大计算机学院院标,计算机学院召开2021年国家自然科学基金申报动员会
- 错误程序致广大证券被罚5.2亿
- Altium Designer 20 原理图和PCB网络颜色分配
- vs配置ffmpeg
热门文章
- 天津滨海服务外包产业园年底投入使用
- Android10定制Google开机向导
- 视频 | 你不知道的开源60年秘史
- gitee网站中项目的评论爬取(selenium)
- 一步步将Windows 10装入移动硬盘中(不用Windows To Go)
- 反馈系统ER图,功能设计(未实现)
- 切面的优先级、重(chong)用切点表达式
- [ECCV 2020] Distribution-balanced loss for multi-label classification in long-tailed datasets
- 华为nova4e能更新鸿蒙吗,华为nova4e禁止系统更新的方法_怎么关闭和禁用系统更新功能...
- 华硕K42J触摸屏禁用