关于线程池你不得不知道的一些设置
作者: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


猜你喜欢
- ArrayList简介:ArrayList实现了List接口它是一个可调整大小的数组可以用来存放各种形式的数据。并提供了包括CRUD在内的多
- 水印种类及功能介绍 PDF水印分为两种:文本水印和图片水印。文本水印一般被用在商业领域,提醒读者该文档是受版权保护的,其他人不能抄
- 在《Android Handler之消息循环的深入解析》中谈到了Handler是用于操作线程内部的消息队列,所以Handler可以用来线程间
- 需要读取excel数据转换成json数据,写了个测试功能,转换正常:JSON转换:org.json.jar 测试类:
- Mybatis typeAlias标签在实际的工程之中,类的全限定名称很长,当我们需要大量使用的时候,这非常不方便的,然而mybatis提供
- 1)打开idea,开始创建SpringBoot项目2)选择 Spring Initializr ,选择合适的jdk版本,点击Next在操作到
- 本文实例讲述了Android桌面组件App Widget用法。分享给大家供大家参考。具体如下:Android开发应用除了程序应用,还有App
- 安卓的开发从布局开始。安卓的界面编写也是使用xml进行布局的,一般如果熟悉了html界面的布局,那么很容易就能够理解安卓有关的布局了,这里介
- 老风格,废话不多说了,直接给大家贴java代码了。代码如下:package com.zzw.getPhoneInfos;import and
- 有人问我,怎么判断一个点是不是在多边形内,本来想着把这个多边形分成一个又一个三角形,如图, 然后判断这个点是不是在某个三角形中,如
- 1 场景启动器 starter 简介什么是 SpringMVC在早期 Java Web 的开发中,统一把显示层、控制层、数据层的操作全部交给
- 在Android实现没有标题栏的方法有两种:在代码中添加requestWindowFeature(Window.FEATURE_NO_TIT
- 实例如下://选择文件,点击【浏览】,选择文件 private void button1_Click(object sender, Even
- 详解java中的PropertyChangeSupport与PropertyChangeListenerjava中的PropertyChan
- 一、多线程的优缺点多线程的优点:1)资源利用率更好2)程序设计在某些情况下更简单3)程序响应更快多线程的代价:1)设计更复杂虽然有一些多线程
- 一. 引用的概念引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
- 1.类的属性 filed1)在kotlin中定义属性,必须赋初始值,要不编译器检查不通过。这个和java不同2)kotlin会针对于定义的每
- 1、为什么使用缓存  我们知道内存的读取速度远大于硬盘的读取速度。当需要重复地获取相同数据时,一次一次地
- 下文笔者讲述StringTokenizer对象的简介说明,如下所示StringTokenizer的简介Java StringTokenize
- 表单的重复提交: 没有完整的进行一次,先请求表单页面->再提交表单过程而完成数据提交造成的根本原因: 没有完整的进行一次,先请求表单页