初级线程池使用
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.execute(() -> { // 无返回值
System.out.println("Hello World");
});
executor.submit(() -> { // 有返回值
System.out.println("Hello World");
return "123";
}).get();
executor.shutdown();
但是老程序员一般不建议你用Executors创建线程池,阿里的开发文档明确提出不允许使用Executors创建线程池。其实并不是说Executors有多危险,而是太过于简单,并不能让初学者了解到线程池各个参数的意义,有资源耗尽的风险。所以一般都推荐直接使用下面的方式创建线程池。
成熟期线程池使用
ThreadPoolExecutor threadPoolExecutor =
new ThreadPoolExecutor(4, 8, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000));
threadPoolExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略
// do something……
threadPoolExecutor.shutdown();
直接使用ThreadPoolExecutor构造器创建线程池,能够清除的看到线程池的几个主要参数,稍加了解之后就能够清除线程池的运行规则,避免资源耗尽的风险。
完全体线程池使用
在企业中,可能有很多地方用到线程池,特别是一些次要流程,这时候就需要一个公用线程池,不必频繁的新建和关闭。要注意,高并发的主流程最好使用一个专用线程池。
这是我目前在用的通用线程池,附带每个参数的详细解释:
@Configuration
public class ThreadExecutorConfig {
@Bean(name = "commonExecutor")
public ThreadPoolTaskExecutor commonExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//线程池创建时候初始化的线程数﹣与cpu核数保持接近,线程并发控制在8,避免并发过高
executor.setCorePoolSize(8);
//线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(16);
//用来缓冲执行任务的队列
executor.setQueueCapacity(1000);
//允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁用来设置线程池,以确保应用最后能够被关闭,而不是阻塞住executor.setAwaitTerminationSeconds(SHFW_AWAIT_TERMINATION_SECONDS);
executor.setAwaitTerminationSeconds(60);
//线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
executor.setThreadNamePrefix("oneExecutor-");
//线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean,这样这些异步任务的销毁就会先于线程池的销毁。
executor.setWaitForTasksToCompleteOnShutdown(true);
//rejection-policy:当pool已经达到max size的时候,如何处理新任务
//CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
/*线程池对拒绝任务的处理策略:这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,
该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务*/
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//填充装饰器﹣用来设置线程的上下文
//executor.setTaskDecorator(new ContextCopyingDecorator());
//执行初始化
executor.initialize();
return executor;
}
为什么注释掉这一行://executor.setTaskDecorator(new ContextCopyingDecorator());
因为这个装饰器参数一般不要设置,当然用了必然锦上添花,只是这个玩意有点复杂,我会在下一篇展开说说:【详解优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器】