系统性能优化的几种常用手段是异步和缓存。因此我们常常使用线程池异步处理一些业务。
线程池的使用还是相对比较简单的,首先创建一个线程池,然后通过execute或submit执行任务。
但魔鬼往往藏于细节之中,稍有不慎就会出错。本文将会详细总结线程池容易出错的五大坑
一、拒绝策略参数知多少
二、拒绝策略使用不当,系统阻塞不可用
三、多任务get()异常时,结果获取有误
四、ThreadLocal与线程池搭配使用,上下文缺失
五、父子任务共用同一线程池,系统“饥饿”死锁
以下为线程池的核心流程【具体内容参考:线程池原理】
一、拒绝策略参数知多少
我们都知道,当任务过多,线程池处理不过来时会被拒绝,进入拒绝策略
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
通过实现RejectedExecutionHandler,就可以作为线程池的拒绝策略使用。
目前官方提供了四种拒绝策略,分别为:
- CallerRunsPolicy:由任务调用方执行
- AbortPolicy:抛出异常,同样也是由任务调用方处理异常
- DiscardPolicy:丢弃当前任务
- DiscardOldestPolicy:丢弃队列中最老的任务,并执行当前任务
线程池有execute和submit两种方法执行任务:
execute执行我们最原始的任务;
而submit则不同,先是将我们最原始的任务封装成FutureTask任务,然后将FutureTask任务交由execute执行
线程池拒绝策略中Runnable r就是execute执行的任务,因此当使用r时就要注意它是我们最原始的任务还是FutureTask任务
二、拒绝策略使用不当,系统阻塞不可用
前面我们讲到submit方法执行任务时