执行任务的时候,先使用corePoolSize范围内的线程,多出的任务放到阻塞队列,阻塞队列放满了,才会用maximumPoolSize范围内的线程。
如果阻塞队列设置成无界,有OOM的风险。
线程/线程池一定要起好名字,这样用jstack导出threaddump文件后,可以看出线程是在执行什么业务。
慎用showdownNow(),会直接把业务线程停掉,可能对业务造成影响。
自定义线程池,最好有固定的后缀,与框架的线程池区分开,这样jstack的时候可以grep 固定后缀,只看自定义线程的信息。
ExecutorService executorService = new ThreadPoolExecutor(10, 20, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>(10), factory, new ThreadPoolExecutor.AbortPolicy());
线程池配置如上,如果有22个任务,第1-10个任务由核心线程跑,第11-20个任务放入阻塞队列,第21-22个任务由新建线程(或者释放cpu资源后的核心线程)跑,第21-22个任务的执行顺序,先于第11-20个任务。
当运行中的线程数已达到最大线程数,且阻塞队列已满时,对于新的任务,有以下策略
CallerRunsPolicy: 把新的任务交给main线程执行
DiscardPolicy: 直接舍弃
AbortPolicy: 抛异常,不影响其他线程的执行,该策略可能导致线程无法释放
DiscardOldestPolicy: 舍弃最旧的任务
public static void main(String[] args) throws Exception{
ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("test-thread-pool-%d").build();
ExecutorService executorService = new ThreadPoolExecutor(10, 20, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>(10), factory, new ThreadPoolExecutor.AbortPolicy());
for (int i = 1; i <= 31; i++) {
final int j = i;
executorService.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": " + j);
}
});
}
executorService.shutdown();
}
上述代码,线程无法正常释放,jstack导出的dump日志大量waiting on condition
网友评论