目录
前言
线程池是一个很重要的面试点。
面试前总是看看各种文章,好多时候都觉得筛选文章好坏好浪费时间。
不如自己整理整理,记记笔记,看看代码底层。
面试关心:
1. 参数都是什么意思
2. 有几种线程池,几种拒绝策略
3. 底层源码
4. 有哪些坑,线上遇到过问题吗?
线程池的参数含义
/**
* 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
核心线程数
- 线程池中会维护一个最小的线程数量,即使核心线程会一直存活,可能会空闲,也不会被销毁(除非设置allowCoreThreadTimeout)
- 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭
-
* @param corePoolSize the number of threads to keep in the pool, even * if they are idle(空闲), unless {@code allowCoreThreadTimeOut} is set
- 官方英文释义:等于前两点
maximumPoolSize
-
* @param maximumPoolSize the maximum number of threads to allow in the * pool
- 英文释义:最大的数量可以允许在这个线程池的
- 一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后面会介绍)中,如果工作队列满了,才会创建一个新线程
- 其实好理解点就是:这东西是先看核心线程数,比如10个,这个maximumPoolSize假如是20,那么任务先会缓存到工作队列里面,工作队列满了,才会额外开启线程,如果是20那么最多开启10个
- 然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。
- maximumPoolSize最佳实践是跟核心线程数保持一致
keepAliveTime
-
* @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.
- 中文释义:线程数,大于核心数,那部分线程会过期
- 一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定
- 核心线程不受这个影响
unit
-
* @param unit the time unit for the {@code keepAliveTime} argument
- keepAliveTime的时间单元:没啥好说的
workQueue
-
* @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.
- 中文释义:队列用于任务执行,可以Runnable的方法进行执行之后,会先放到这个队列里面
- 新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。
有几种工作队列?
1.ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。
有界数组,如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
2.LinkedBlockingQuene
基于链表的无界阻塞队列,最大容量为Integer.MAX( 2147483647),按照FIFO排序。
相当于无界队列了,还是注意使用。参数maxPoolSize其实是不起作用的(因为不管咋样,工作队列是放不满的)。
/**
* A constant holding the minimum value an {@code int} can
* have, -2<sup>31</sup>.
*/
@Native public static final int MIN_VALUE = 0x80000000;
/**
* A constant holding the maximum value an {@code int} can
* have, 2<sup>31</sup>-1.
*/
@Native public static final int MAX_VALUE = 0x7fffffff;
3.SynchronousQueue
灭有缓存,直接就进来就用空闲线程执行,没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
4.PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
BlockingQuene的引用
ThreadFactory
负责生产线程
项目中其实用的是磨人的线程工厂,和默认的拒绝策略。
* Constructs a new {@code Thread}. Implementations may also initialize * priority, name, daemon status, {@code ThreadGroup}, etc.
也就是说,可以设定线程的优先级,名称 和守护线程等。
/**
* The default thread factory
*/
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
其实就是设置守护线程和优先级。
handler
拒绝策略:
/** * The default rejected execution handler */ private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
CallerRunsPolicy
/**
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded.
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
直接跑被拒绝任务的run方法,,除非线程池已经shutdown,则直接抛弃任务。
AbortPolicy
/**
* A handler for rejected tasks that throws a
* {@code RejectedExecutionException}.
*/
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
Always throws RejectedExecutionException.
任务就丢弃了
DiscardPolicy
/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
Does nothing, which has the effect of discarding task r.
啥都不做,直接丢弃任务
DiscardOldestPolicy
/**
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded.
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
抛弃进入队列最早的任务
尝试把这次拒绝的任务放入队列
关于线程池配置?
关于线程池配置这部分转自:https://blog.youkuaiyun.com/sxllllwd/article/details/100533788
作者:沙漏dan
CPU密集型和IO密集型
CPU密集型也是指计算密集型,大部分时间用来做计算逻辑判断等CPU动作的程序称为CPU密集型任务。该类型的任务需要进行大量的计算,主要消耗CPU资源。
这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。(减少任务切换)
IO密集型任务指任务需要执行大量的IO操作,涉及到网络、磁盘IO操作,对CPU消耗较少。
结论:
CPU密集型任务应配置尽可能小的线程,如配置CPU数目+1个线程的线程池。
由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如2*CPU数目。
阿里的线程池推荐
其实就是用底层的代码,自己设置参数。
比如:
Executors工厂创建线程池,不支持自定义拒绝策略。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}