1.说一说你使用的是什么样的线程池,自定义线程池的几个参数是什么
一般使用默认线程池,
有四种线程池:
- newCacheThreadPool 缓存线程池,超出部分如果有空闲部分则回收,如果没有则新建
- newFixedThreadPool (共享无界)提交一个开辟一个新线程,如果满了就进入到线程池队列
- newSingleThreadPool 单线程线程池,确保FIFO线程执行,一个线程结束会顺序执行下一个
- newScheduleThreadPool 定长线程池,支持定时的周期的执行线程策略
注意如果存在无界的线程池或者无界的队列很容易出现outOfMemoryException内存溢出情况,报错
研究线程池原理,发现创建线程池时线程的阻塞队列默认是先进先出的链表队列(默认无界,可选有界),如果不设置大小,默认为int的max值。线程的处理速度没有nio读取速度快,导致阻塞队列一直在增加,最后的结果就是内存溢出。
解决方法:设置队列大小,修改任务拒绝策略。每行必须处理故选择由调用线程处理该任务!
参数介绍:
corePoolSize--池中所保存的线程数,包括空闲线程。
maximumPoolSize--池中允许的最大线程数。
keepAliveTime-- 空闲线程等待工作的超时时间, allowCoreThreadTimeOut默认是false,如果需要回收coreThread需要开启
/** * Timeout in nanoseconds for idle threads waiting for work. * Threads use this timeout when there are more than corePoolSize * present or if allowCoreThreadTimeOut. Otherwise they wait * forever for new work. */
Unit--(keepAliveTime 参数的时间单位)
workQueue--执行前用于保持任务的队列。此队列仅保持由 execute方法提交的 Runnable任务。
threadFactory--执行程序创建新线程时使用的工厂。
Handler--由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
rejectHandler :当拒绝处理任务时的策略
https://www.cnblogs.com/wang-meng/p/10163855.html
关于线程池中的corePoolSize大小:
1.首先看是计算密集型还是IO密集型
计算密集型:减少cpu上下文切换时间,最好是cpu+1,时间片轮转
为什么是+1操作:java并发编程中提到,如果cpu的叶缺失或者其他原因暂停,这个额外的线程能保证线程时钟周期不会被浪费
IO密集型:一般为cpu/(1-阻塞系数) 一般就是比线程数多 2*cpu
三种队列:
workQueue = new ArrayBlockingQueue<>(5);//基于数组的先进先出队列,bounded(有界)
workQueue = new LinkedBlockingQueue<>();//基于链表的先进先出队列,optionally-bounded( 可选有界 )
workQueue = new SynchronousQueue<>();//无缓冲的等待队列,无界
四种拒绝策略:(可自定义拒绝策略)
AbortPolicy:直接抛出异常阻止系统正常工作。(默认)
CallerRunsPolicy:只要线程池未关闭,添加失败,主线程会调用执行器的execute方法来执行任务
DiscardOldestPolicy:丢弃最老的一个请求,尝试再次提交任务。
DiscardPolicy:丢弃无法处理的任务,不给予任何处理。
2.说一说线程池的执行流程--引用林冠宏图
ThreadPool一般执行流程
newFixedThreadPool(无界队列) 对比上一个执行流程,主要是队列是否饱和,执行拒绝策略
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(
nThreads, // corePoolSize
nThreads, // maximumPoolSize == corePoolSize
0L, // 空闲时间限制是 0
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>() // 无界阻塞队列
);
}
newCacheThreadPool执行流程
public static ExecutorService newCachedThreadPool(){
return new ThreadPoolExecutor(
0, // corePoolSoze == 0
Integer.MAX_VALUE, // maximumPoolSize 非常大
60L, // 空闲判定是60 秒
TimeUnit.SECONDS,
// 神奇的无存储空间阻塞队列,每个 put 必须要等待一个 take
new SynchronousQueue<Runnable>()
);
}
newSingleThreadPool
public static ExecutorService newSingleThreadExecutor() {
return
new FinalizableDelegatedExecutorService//代理
(
new ThreadPoolExecutor
(
1,
1,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory
)
);
}
3.你的项目中定义的核心线程数、最大线程数和队列大小是如何定义的?
根据美团技术团队文章,可以使用线程池参数动态化,一个线程池在运行过程中是可以修改核心线程数和最大线程数的且不用重启线程
面试题1:
假设你的线程池参数是 10 30 x x 1000 x x
一时间进入了30个耗时操作的线程,那么最多多少线程等待,到多少位进行拒绝策略
30+1000 = 1030个最多 1001个进行拒绝策略
面试题2:
线程池被创建后里面有线程吗?如果没有的话,你知道有什么方法对线程池进行预热吗?
线程池被创建后如果没有任务过来,里面是不会有线程的。如果需要预热的话可以调用下面的两个方法:
1.全部启动
2.仅启动一个
面试题3:核心线程数可以回收么,如果不能使用什么方法?
默认不回收,需要调用方法
允许核心线程超时,默认为false,需要开启为true
附上原笔记