背景介绍:
面试中经常会被问起多线程相关的问题,这个系列会汇总多线程相关的问题。本文源自总结了滴滴和阿里面试过程提问过的线程池问题,欢迎留言评论。
- 聊一聊基础线程池构造函数的参数有哪些,各自都代表什么含义?
先来看一下Java源码里的定义
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
简单介绍下构造的参数:
- corePoolSize:核心线程数
- maximumPoolSize:最大线程数
- keepAliveTime:线程存活时间(注意是超过核心线程数的线程的存活时间,核心线程一旦被创建不会)
- workQueue:任务队列(阻塞队列)
- handler:拒绝策略
- 你能说一下线程初始化操作吗?
默认情况下,只有提交任务时,才会创建线程(包括核心线程)。但是我们可以使用一些骚操作来让线程池创建后立刻创建线程,涉及两个方法如下:
public boolean prestartCoreThread() {
return workerCountOf(ctl.get()) < corePoolSize &&
addWorker(null, true);
}
public int prestartAllCoreThreads() {
int n = 0;
while (addWorker(null, true))
++n;
return n;
}
两者的区别是:prestartCoreThread()
只会创建一个线程,prestartAllCoreThreads()
会创建所有的核心线程。用法示例:
ThreadPoolExecutor executorService = new ThreadPoolExecutor(taskCorePoolSize,
taskMaxPoolSize,
taskKeepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
new ThreadPoolExecutor.AbortPolicy());
executorService.prestartCoreThread();
executorService.prestartAllCoreThreads();
- 这个线程存活时间指的什么,核心线程能被回收吗?
线程存活时间指的是超过核心线程数的线程,当空闲时,超过指定时间则会销毁该线程。但默认核心线程不会被回收。如需回收,可以使用allowCoreThreadTimeOut()
方法。源码定义如下:
public void allowCoreThreadTimeOut(boolean value) {
if (value && keepAliveTime <= 0)
throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
if (value != allowCoreThreadTimeOut) {
allowCoreThreadTimeOut = value;
if (value)
interruptIdleWorkers();
}
}
注意:谨慎使用该方法,毕竟线程的创建和销毁都是有开销的。
- 你能说一下任务队列都有哪些吗?
- ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
- LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
- synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。
特别注意:线上不允许使用无界队列,不仅会造成内存大量占用频繁的触发Full GC,更严重的可能造成OOM。
- 刚刚说到拒绝策略,能说一下什么情况会触发拒绝策略吗?常见的拒绝策略都有哪几种呢。
当任务队列满了、最大线程数也满了以后,再来任务则会执行拒绝策略。java的默认拒绝策略有以下几种:
-
AbortPolicy()
直接抛出运行时异常,请注意,该策略会中断调用者的处理过程。 -
CallerRunsPolicy()
任务将由调用者的线程来执行,不再占用线程池中的线程数。 -
DiscardPolicy()
任务直接丢弃掉(不会抛异常) -
DiscardOldestPolicy()
删除队列里的最久未处理的任务,并将拒绝任务加入到队尾。这里需要搞清楚是删除的是队列的头部元素。
为了避免引起歧义,附上Java源码
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}