需求
1、需要起异步线程。
2、需要获取到异步线程的执行结果。
3、需要在所有异步线程执行结束时,实时获取到所有异步线程的处理结果。
4、当没有充裕的线程资源时,不能因为等待线程资源而造成原有业务的阻塞,能够自动进行降级处理,切回原有的串行处理模式。
方案
线程池 + FutureTask
使用FutureTask的原因:
1、FutureTask可以获取到异步线程的执行结果。
2、FutureTask的get方法可以阻塞主线程,在异步线程执行结束后继续执行主线程。这点和CountDownLatch的作用一样。相比于CountDownLatch,FutureTask使用起来更简单,代码侵入也更少,使用CountDownLatch需要根据业务中所起的线程数设置计数器大小,并且需要在线程执行结束进行计数器减一处理。
线程池设计
线程池参数设置
核心线程数:cpu核心数量
最大线程数:cpu核数核心数量 * 2
超时时间:10分钟(超出核心线程数之外的线程,空闲时间超时后进行回收)
阻塞队列大小:10 (对于同步请求使用多线程的场景,阻塞队列如果设置太长,当并发请求量比较高时,会导致过多任务进入阻塞队列等待,不能及时处理响应,所以这里设置小一点,配合拒绝策略,在高并发时实现并行改串行处理)
拒绝策略:调用主线程进行处理 (超出最大线程数,并且阻塞队列也满了之后,执行拒绝策略,使用主线程处理,实现并行改串行处理)
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(10),
new ThreadFactoryBuilder().setNameFormat("future-task-pool-%d").build(), new ThreadPoolExecutor.CallerRunsPolicy());
注意:
1、阻塞队列一定要使用有界队列,如果不限制大小,高并发时会有大量任务持续加入队列,可能会导致OOM。
2、线程池默认的拒绝策略是AbortPolicy,超出最大线程并超出阻塞队列时,会直接抛异常,不建议使用AbortPolicy。如果使用AbortPolicy,需要对异常进行捕获,并且做相应的补偿处理。