目的是:
通过实现AsyncConfigurer自定义线程池,包含异常处理
实现AsyncConfigurer接口对异常线程池更加细粒度的控制
*a) 创建线程自己的线程池
b) 对void方法抛出的异常处理的类AsyncUncaughtExceptionHandler
个人初步理解
一、线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时间,
从而提高效率。如果一个线程的时间非常长,就没必要用线程池了(不是不能作长时间操作,而是不宜。),
况且还不能控制线程池中线程的开始、挂起、和中止。
二、利用线程池启动线程
Thread udpThread = new Thread(udp);
poolTaskExecutor.execute(udpThread);
获取当前线程池活动的线程数:
int count = poolTaskExecutor.getActiveCount();
logger.debug("[x] - now threadpool active threads totalNum : " +count);
三、配置解释
当一个任务通过execute(Runnable)方法欲添加到线程池时:
1、 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2、 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
3、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
4、 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程 maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
5、 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
allowCoreThreadTimeout:允许核心线程超时
rejectedExecutionHandler:任务拒绝处理器
两种情况会拒绝处理任务:
当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务
当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务
线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常
ThreadPoolExecutor类有几个内部实现类来处理这类情况:
AbortPolicy 丢弃任务,抛运行时异常
CallerRunsPolicy 执行任务
DiscardPolicy 忽视,什么都不会发生
DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
实现RejectedExecutionHandler接口,可自定义处理器
废话不多说 上代码 环境:IDEA +SpringBoot+maven
一、线程池配置类实现AsyncConfigurer 接口:
@Component public class MyAsyncConfigurer implements AsyncConfigurer { private static final Logger log = LoggerFactory.getLogger(MyAsyncConfigurer.class); public Executor getAsyncExecutor() { ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor(); threadPool.setCorePoolSize(2);//当前线程数 threadPool.setMaxPoolSize(120);// 最大线程数 threadPool.setQueueCapacity(1);//线程池所使用的缓冲队列 threadPool.setWaitForTasksToCompleteOnShutdown(true);//等待任务在关机时完成--表明等待所有线程执行完 threadPool.setAwaitTerminationSeconds(60 * 15);// 等待时间 (默认为0,此时立即停止),并没等待xx秒后强制停止 threadPool.setThreadNamePrefix("MyAsync-");// 线程名称前缀 threadPool.initialize(); // 初始化 System.out.println("--------------------------》》》开启异常线程池"); return threadPool; } public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new MyAsyncExceptionHandler(); } /** * 自定义异常处理类 * @author hry * */ class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler { //手动处理捕获的异常 @Override public void handleUncaughtException(Throwable throwable, Method method, Object... obj) { System.out.println("-------------》》》捕获线程异常信息"); log.info("Exception message - " + throwable.getMessage()); log.info("Method name - " + method.getName()); for (Object param : obj) { log.info("Parameter value - " + param); } } }
二、使用简单无参数异步线程进行测试
@Override @Async public String asyncMethodWithVoidReturnType() { System.out.println("线程名称:"+Thread.currentThread().getName() + " be ready to read data!"); try { Thread.sleep(1000 * 5); System.out.println("---------------------》》》无返回值延迟3秒:"); } catch (InterruptedException e) { e.printStackTrace(); } return "已进入到异步"; }
三、0--3秒内连续访问如下结果
线程名称:MyAsync-1 be ready to read data! 线程名称:MyAsync-2 be ready to read data! 线程名称:MyAsync-3 be ready to read data! 线程名称:MyAsync-4 be ready to read data! 线程名称:MyAsync-5 be ready to read data! 线程名称:MyAsync-6 be ready to read data! 线程名称:MyAsync-7 be ready to read data! 线程名称:MyAsync-8 be ready to read data! MyAsync-1---------------------》》》无返回值延迟5秒: 线程名称:MyAsync-1 be ready to read data! MyAsync-2---------------------》》》无返回值延迟5秒: 线程名称:MyAsync-2 be ready to read data! MyAsync-3---------------------》》》无返回值延迟5秒: MyAsync-4---------------------》》》无返回值延迟5秒: MyAsync-5---------------------》》》无返回值延迟5秒: MyAsync-6---------------------》》》无返回值延迟5秒: MyAsync-7---------------------》》》无返回值延迟5秒: MyAsync-8---------------------》》》无返回值延迟5秒: MyAsync-1---------------------》》》无返回值延迟5秒: MyAsync-2---------------------》》》无返回值延迟5秒:
如上可以看出 线程池发挥作用 多个线程访问如果超过核心线程数+队列数 变新创建线程,如果有线程
空闲下来会继续分配,以此来提高效率。我是利用postMain 来进行的测试,以验证上面所说的理论问题。。
实际应用中比这个复杂得多。。。。。。。。
四、如果想要手动捕获异常信息 如下代码 即可
throw new IllegalArgumentException(s);
将上面代码 放在需要捕获信息的中
在线程池配置中 MyAsyncExceptionHandler 的方法中 即会捕捉到信息。。。。。。