代码改变世界

SpringBoot 线程池配置 实现AsyncConfigurer接口方法

2018-05-23 10:20  晨曦曙光  阅读(17529)  评论(1编辑  收藏  举报

 

 

 目的是:
  通过实现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 的方法中 即会捕捉到信息。。。。。。