Springboot @Async及线程池的使用和扩展

最近在看阿里的JAVA开发手册,说到 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程(new Thread()这种)。

首先在springboot中,它已经给我们提供了很方便的异步和线程池机制。实现异步只要加一个注解@Async,就可以实现了

 

阿里的JAVA开发手册还说到:使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题

 

Spring是通过任务执行器(TaskExecutor)来实现多线程和并发编程,使用ThreadPoolTaskExecutor来创建一个基于线城池的TaskExecutor

 

 

1、开启异步任务

本文springboot版本:

2.0.6.RELEASE

在启动类上加注解

@EnableAsync

2、配置类 TaskPoolConfig

@Configuration
public class TaskPoolConfig {
    @Bean("taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();

        //最大线程数30
        executor.setPoolSize(30);
        //线程池名的前缀
        executor.setThreadNamePrefix("taskExecutor-");
        //设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
        executor.setWaitForTasksToCompleteOnShutdown(true);
        //设置线程池中任务的等待时间
        executor.setAwaitTerminationSeconds(60);
        //当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        return executor;
    }
}

值得留意的是  上面的方法名称为taskExecutor

这里我没有用网上更常见的ThreadPoolTaskExecutor 而是用了ThreadPoolTaskScheduler

注意:

ThreadPoolTaskExecutor 和ThreadPoolTaskScheduler都是ThreadPoolExecutor的包装

区别是ThreadPoolTaskScheduler实现了TaskScheduler接口,仅仅多实现这个接口

 

3、改造service层服务

编写一个测试接口CommonService ,和其实现类CommonServiceImpl 

public interface CommonService {

    void executeAsync();

}
@Service
public class CommonServiceImpl implements CommonService {

    @Async("taskExecutor")
    @Override
    public void executeAsync() {
        for (int i=0;i<6;i++) {
            System.out.println( "现在i的值--->>:"+i );
            try{
                Thread.sleep(1000);
            }catch(Exception e){
                e.printStackTrace();
            }
            
        }
       
    }


}

 @Async("taskExecutor")     表明executeAsync方法进入的线程池是taskExecutor方法创建的

 

4、TestController

@RestController
public class TestController {

    @Autowired
    CommonService commonService;

    @GetMapping("/test/executeAsync")
    public String testTASK() {
        commonService.executeAsync();
        return "http请求已结束";
    }

}

 

5、测试

我们用浏览器来测试一下:

发现当浏览器显示上述提示时,控制台还在打印以下信息,表明该方法以及异步

6、拓展

  虽然现在线程池能其作用,但是还不清楚线程池的具体使用情况,有多少线程在执行,多少在队列中等待呢?所以创建了一个ThreadPoolTaskExecutor的子类,在每次提交线程的时候都会将当前线程池的运行状况打印出来。这部分参考了这篇博客

https://blog.csdn.net/boling_cavalry/article/details/79120268

public class VisiableThreadPoolTaskScheduler extends ThreadPoolTaskScheduler {

    private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskScheduler.class);

    private void showThreadPoolInfo(String prefix){
//        ThreadPoolExecutor threadPoolExecutor = getScheduledExecutor();
        ScheduledThreadPoolExecutor threadPoolExecutor = getScheduledThreadPoolExecutor();

        if(null==threadPoolExecutor){
            return;
        }

        logger.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(),
                prefix,
                threadPoolExecutor.getTaskCount(),
                threadPoolExecutor.getCompletedTaskCount(),
                threadPoolExecutor.getActiveCount(),
                threadPoolExecutor.getQueue().size());
    }

    @Override
    public void execute(Runnable task) {
        showThreadPoolInfo("1. do execute");
        super.execute(task);
    }

    @Override
    public void execute(Runnable task, long startTimeout) {
        showThreadPoolInfo("2. do execute");
        super.execute(task, startTimeout);
    }

    @Override
    public Future<?> submit(Runnable task) {
        showThreadPoolInfo("1. do submit");
        return super.submit(task);
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
        showThreadPoolInfo("2. do submit");
        return super.submit(task);
    }

    @Override
    public ListenableFuture<?> submitListenable(Runnable task) {
        showThreadPoolInfo("1. do submitListenable");
        return super.submitListenable(task);
    }

    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
        showThreadPoolInfo("2. do submitListenable");
        return super.submitListenable(task);
    }


}

注意这里是:

getScheduledThreadPoolExecutor()

然后修改上面的 TaskPoolConfig  文件

//        ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
        ThreadPoolTaskScheduler executor = new VisiableThreadPoolTaskScheduler();

 

我们不停的刷新浏览器进行测试,

2019-04-27 13:20:51.693  INFO 18228 --- [nio-8080-exec-7] c.z.o.c.VisiableThreadPoolTaskScheduler  : taskExecutor-, 2. do submit,taskCount [88], completedTaskCount [60], activeCount [28], queueSize [0]
现在i的值--->>:0

这说明提交任务到线程池的时候,调用的是submit(Callable task)这个方法,当前已经提交了88个任务,完成了60个,当前有28个线程在处理任务,还剩0个任务在队列中等待,线程池的基本情况一路了然:P

 

参考:https://www.carryingcoder.com/2017/12/22/Spring-%E5%BC%82%E6%AD%A5%E6%89%A7%E8%A1%8C-Async-%E3%80%81%E4%BB%BB%E5%8A%A1%E8%B0%83%E5%BA%A6-Schedule/

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小牛呼噜噜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值