线程池
线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runnable或Callable接口的实例对象。
新建线程池的方法如下(通过new ThreadPoolExecutor):
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
名词解释如下:
- corePoolSize:核心线程数
- maximumPoolSize:最大线程数
- keepAliveTime:存活时间
- unit:存活时间单位
- workQueue:任务队列
- handler:拒绝策略
线程池需要了解的东西有很多,比如为什么不建议使用Executors工具类创建线程池?线程池的工作流程?线程池的核心线程数如何设置?任务队列如何选择?拒绝策略?
这里不一一深入,感兴趣的小伙伴可以自行网上翻阅资料。
线程池的四大拒绝策略
为什么需要了解拒绝策略?
因为应对不同的场景选择不同的拒绝策略来处理线程池的线程数达到最大值时再加入进来的线程。
有以下四种拒绝策略
- AbortPolicy(默认):当任务被拒绝时,会抛出“RejectedExecutionException”异常。
- DiscardPolicy:当任务被拒绝时,不会抛出异常,而是直接丢弃任务。
- DiscardOldestPolicy:当任务被拒绝时,线程池会丢弃队列中最旧的未执行的任务,然后将被拒绝的任务添加到等待队列当中。
- CallerRunsPolicy:当任务被拒绝时,调用
execute
方法的线程将负责执行该任务。
线程池的默认拒绝策略
如果我们不传入拒绝策略时,将会走以下方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
可以看到,这里默认给的是一个defaultHandler
的拒绝策略,继续深入可以发现以下代码
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
defaultHandler
就是AbortPolicy()
所以得出结论,默认拒绝策略就是AbortPolicy()
四大拒绝策略源码及其使用场景
1、AbortPolicy
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出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());
}
}
这是线程池默认的拒绝策略,在任务不能再提交的时候,抛出异常,及时反馈程序运行状态。如果是比较关键的业务,推荐使用此拒绝策略,这样子在系统不能承载更大的并发量的时候,能够及时的通过异常发现。
2、DiscardPolicy
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。源码解释如下:
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) {
}
}
使用此策略,可能会使我们无法发现系统的异常状态。建议是一些无关紧要的业务采用此策略。例如,某些视频网站统计视频的播放量就是采用的这种拒绝策略。
3、DiscardOldestPolicy
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务。源码解释如下:
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);
}
}
}
此拒绝策略,是一种喜新厌旧的拒绝策略。是否要采用此种拒绝策略,还得根据实际业务是否允许丢弃老任务来认真衡量。
4、CallerRunsPolicy
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 源码解释如下:
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();
}
}
}
如果任务被拒绝了,则由调用线程(提交任务的线程)直接执行此任务(谁提交的谁执行)