一、ThreadPoolTaskExecutor的重要参数
corePoolSize:核心线程数,线程池维护线程的最少数量。
allowCoreThreadTimeOut:是否允许核心线程超时(默认为false)。当为true是,小于等于核心线程数的线程超时会关闭;为false是,小于等于核心线程数的核心线程会一直存活,不管有没有任务。
maxPoolSize:最大线程数量。
keepAliveSeconds:允许空闲时间。如果allowCoreThreadTimeOut为true,则线程空闲时间到达keepAliveSeconds后,该线程会销毁。
queueCapacity:阻塞队列
1.当前线程池中线程数量<corePoolSize,则会新建一个线程执行任务;
2.corePoolSize=<当前线程池中线程数量<maxPoolSize,且阻塞队列还没有满,则将任务放入阻塞队列;
3.corePoolSize=<当前线程池中线程数量<maxPoolSize,且阻塞队列已满,则会新建一个线程执行任务;
4.当前线程池中线程数量>=maxPoolSize,且阻塞队列已满,并且没有空闲的线程执行任务时,有新任务时则会执行拒绝策略;
二、拒绝策略
1.CallerRunsPolicy
如果线程池、队列都满了,添加线程池失败,则主线程会自己去执行任务,不会等待线程池中的线程去执行。
2.AbortPolicy(默认策略)
如果线程池、队列都满了,则丢弃任务,并抛出异常。
3.DiscardPolicy
如果线程池、队列都满了,则丢弃任务,不会抛出异常。
4.DiscardOldestPolicy
如果线程池、队列都满了,则会将最早进入队列的任务删掉,新任务进入队列。
5.自定义策略
如果以上四种策略都不满足业务要求,可以自定义策略。需要实现RejectedExecutionHandler接口,并实现其rejectedExecution方法即可,将具体逻辑写入该方法。
三、合理配置线程池大小
线程池的设置要根据实际需求和使用场景决定,不是配置的越大越好,线程过多会导致内存的高使用量,消耗过多资源。
首先确认CPU核数,然后从线程的应用场景分析是CPU密集型任务,还是IO密集型任务:
1.CPU密集型任务:大部分场景下都是纯CPU计算的任务,比如压缩、解密、算法等一系列消耗CPU资源的任务。一般线程数设置为:CPU核数+1 或者CPU核数*2。
2.IO密集型任务:比如文件读写操作、网络通信、数据库操作等任务,不会特别消耗CPU资源,但是IO任务比CPU任务耗时更多,一般线程数设置为:CPU核数*[1+IO耗时/CPU耗时] 或者CPU核数/(1-0.9)
比如CPU核数4,线程池大小设置为40.
四、配合CountDownLatch
CountDownLatch是一个同步计数器,用来协调多个线程之间的同步。
CountDownLatch初始化为线程的数量n,每当一个任务线程执行完之后调用countDownLatch.countDown()方法,计数器数量减1,当计数器数量变为0时,在countDownLatch上await()的线程,也就是等待的主线程就会被唤醒,继续执行主线程的任务。
五、使用
/*** 当前可用CPU数 */
private static final int availabltProcessors = Runtime.getRuntime().availableProcessors();/*** 线程名称*/
private static ThreadFactory threadName = new ThreadFactoryBuilder().setNameFormat("t-%d").build();/*** 线程池配置*/
private static ThreadPoolExecutor executor = new ThreadPoolExecutor(availabltProcessors * 2, availabltProcessors * 5, 200L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(2000), threadName, new ThreadPoolExecutor.AbortPolicy());public void test() {// 总页数int pageCount = 100;CountDownLatch countDownLatch = new CountDownLatch(pageCount);for (int pageNo = 1; pageNo <= pageCount; pageNo++) {executor.submit(() -> {try {// 分页处理} catch (Exception e) {log.error("分页处理失败...", e);} finally {countDownLatch.countDown();}});}try {countDownLatch.await();} catch (InterruptedException e) {log.error("数据处理失败...", e);}log.info("数据处理完成!");
}