关于线程池你不得不知道的一些设置
作者:Java科代表 发布时间:2021-06-08 11:34:45
看完我上一篇文章,「你都理解创建线程池的参数吗?」之后,当遇到这种问题,你觉得你完全能够唬住面试官了,50k轻松到手。殊不知,要是面试官此刻给你来个反杀:
初始化线程池时可以预先创建线程吗?线程池的核心线程可以被回收吗?为什么?
如果此刻你一脸懵逼,这个要慌,问题很大,50k马上变5k。
有细心的网友早就想到了这个问题:
在ThreadPoolExecutor线程池中,还有一些不常用的设置。我建议如果您在应用场景中没有特殊的要求,就不需要使用这些设置。
初始化线程池时可以预先创建线程吗?
prestartAllCoreThreads
初始化线程池时是可以预先创建线程的,初始化线程池后,再调用prestartAllCoreThreads()方法,即可预先创建corePoolSize数量的核心线程,我们看源码:
public int prestartAllCoreThreads() {
int n = 0;
while (addWorker(null, true))
++n;
return n;
}
private boolean addWorker(Runnable firstTask, boolean core) {
// ..
}
addWorker方法目的是在线程池中添加任务并执行,如果task为空,线程获取任务执行时调用getTask()方法,该方法从blockingQueue阻塞队列中阻塞获取任务执行,因此线程不会释放,留存在线程池中,如果core=true,说明任务只能利用核心线程来执行。
所以该方法会在线程池总预先创建没有任务执行的线程,数量为corePoolSize。
下面我们测试一下:
从测试结果来看,线程池中已经预先创建了corePoolSize数量的空闲线程。
prestartCoreThread
prestartCoreThread()同样可以预先创建线程,只不过该方法只会与创建1条线程,我们来看源码:
public boolean prestartCoreThread() {
return workerCountOf(ctl.get()) < corePoolSize &&
addWorker(null, true);
}
从方法源码可知,如果此时工作线程数量小于corePoolSize,那么就调用addWorker创建1条空闲核心线程。
下面我们测试一下:
从测试结果来看,线程池中已经预先创建了1条空闲线程。
线程池的核心线程可以被回收吗?
你可能会想到将corePoolSize的数量设置为0,从而线程池的所有线程都是“临时”的,只有keepAliveTime存活时间,你的思路也许时正确的,但你有没有想过一个很严重的后果,corePoolSize=0时,任务需要填满阻塞队列才会创建线程来执行任务,阻塞队列有设置长度还好,如果队列长度无限大呢,你就等着OOM异常吧,所以用这种设置行为并不是我们所需要的。
有没有什么设置可以回收核心线程呢?
allowCoreThreadTimeOut
ThreadPoolExecutor有一个私有成员变量:
private volatile boolean allowCoreThreadTimeOut;
如果allowCoreThreadTimeOut=true,核心线程在规定时间内会被回收。
上面我也说了,当线程空闲时会从blockingQueue阻塞队列中阻塞获取任务执行,所以我们来看看是保证核心线程不被销毁的,我们直接定位到源码部位:
java.util.concurrent.ThreadPoolExecutor#getTask:
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
这里的关键值timed,如果allowCoreThreadTimeOut=true或者此时工作线程大于corePoolSize,timed=true,如果timed=true,会调用poll()方法从阻塞队列中获取任务,否则调用take()方法获取任务。
下面我来解释这两个方法:
poll(long timeout, TimeUnit unit):从BlockingQueue取出一个任务,如果不能立即取出,则可以等待timeout参数的时间,如果超过这个时间还不能取出任务,则返回null;
take():从blocking阻塞队列取出一个任务,如果BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的任务被加入为止。
到这里,我们就很好地解释了,当allowCoreThreadTimeOut=true或者此时工作线程大于corePoolSize时,线程调用BlockingQueue的poll方法获取任务,若超过keepAliveTime时间,则返回null,timedOut=true,则getTask会返回null,线程中的runWorker方法会退出while循环,线程接下来会被回收。
下面我们测试一下:
以上所述是小编给大家介绍的java线程池设置详解整合网站的支持!
来源:https://juejin.im/post/5cb30dba5188251b291b963d
猜你喜欢
- 1.组装查询条件组装查询其实很简单,可以支持条件的链式编程:查询用户名包含a,年龄在 10 - 20 之间并且邮箱不为空的用户:@Testv
- 1.最近的项目中,有一个Activity用到Fragment+ViewPager,其中一个fragment中实现了视频播放的功能,包含有Su
- 本文实例为大家分享了MapReduce实现决策树算法的具体代码,供大家参考,具体内容如下首先,基于C45决策树算法实现对应的Mapper算子
- springboot整合mybatis实现数据库更新批处理1.在mapper接口中编写方法/** * 修改book表中的销量和库存
- 前言在写项目的时候经常需要特定的时间做一些特定的操作,尤其是游戏服务器,维护线程之类的,这时候就需要用到定时器。如果此时你刚好用的是spri
- 前言;Apache common-pool对象池介绍:对象生命周期、Config详解、代码说明对象生命周期Config详解maxActive
- 一:什么是SparkSQL?(一)SparkSQL简介Spark SQL是Spark的一个模块,用于处理结构化的数据,它提供了一个数据抽象D
- 前言请看下面几个问题Spring为什么不推荐使用@Autowired 注解?为什么推荐使用@Resource 代替&nb
- 本文实例为大家分享了Java操作qq邮箱发送邮件的具体代码,供大家参考,具体内容如下今天尝试了使用QQ邮箱的POP3/IMAP/SMTP/E
- @RequestBody部分属性丢失问题描述JavaBean实现public class VerifyNewFriendApplyReq i
- 如下所示:package com.unionx.wanxue; import java.util.Map; import java.util
- 1、volley 项目地址 https://github.com/smanikandan14/Volley-demo (1)&nb
- 本文为大家分享了如何使用eclipse创建java项目,供大家参考,具体内容如下首先,打开Eclipse,在工具栏依次点击【File】>
- 相信大家在系统学习jvm的时候都会有遇到过这样的问题,散落的jvm知识点知道很多,但是真正在线上环境遇到一些莫名其妙的gc异常时候却无从下手
- MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。spri
- 文章来源:互联网 作者:skywoo/CSDNWindows2000+Apache2.0.48+resin2.1.6 &nbs
- CAS原理在计算机科学中,比较和交换(Compare And Swap)是用于实现多线程同步的原子指令。 它将内存位置的内容与给定值进行比较
- 本文实例讲述了java基于递归算法实现汉诺塔问题。分享给大家供大家参考,具体如下:package test;import java.util
- 使用ProcessBuilder踩到的坑最近使用ProcessBuilder执行命令,命令内容正确,但始终报错命令实行失败,是因为不熟悉Pr
- 多继承指一个子类能同时继承于多个父类,从而同时拥有多个父类的特征,但缺点是显著的。1.若子类继承的父类中拥有相同的成员变量,子类在引用该变量