栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

618来袭,大量优惠券生成太耗时?ThreadPoolTaskExecutor线程池来帮你。

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

618来袭,大量优惠券生成太耗时?ThreadPoolTaskExecutor线程池来帮你。

一. 问题阐述

最近威哥一个已经参加工作的学生,要给他们公司迭代开发一个营销活动,其中有一个功能是:当运营人员录入一次活动的同时,需要给这次活动生成一定数量的礼品兑换券。

威哥的这个学生,虽然把这个功能实现了,但效果却不尽理想,于是他就跑来咨询我该怎么进行优化。威哥思索了一下,就结合他这个项目的实际场景,考虑到要生成大数量礼品券的过程比较耗时,于是威哥就决定把生成礼品券的逻辑使用线程进行异步执行。

最终威哥选择使用Spring下的线程池工具类ThreadPoolTask Executor来创建线程,经过这样一番技术优化后,威哥就帮这个学生圆满地完成了他们公司布置的任务。

那么接下来威哥就给大家介绍一下刚才提到的ThreadPoolTask Executor的使用方法和注意事项。

二. 快速使用

1. 创建项目入口类

首先我们创建一个入口类,并在该入口类上添加@EnableAsync注解。

@SpringBootApplication
@EnableAsync
public class AsyncdemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(AsyncdemoApplication.class, args);
    }
}   

2. 编写业务方法

接下来我们把需要异步执行的业务逻辑单独抽取成一个方法,在方法上也加上@Async注解。

@Slf4j
@Service
public class AsyncService {

    
    @Async
    public void asyncProcess() throws InterruptedException {
        log.info("异步任务, 当前线程的名字是 -> {}",
                Thread.currentThread().getName());
    }

    
    @Async
    public Future asyncProcessHasReturn() throws InterruptedException {
        log.info("异步任务(有返回值), 当前线程的名字是  -> {}",
                Thread.currentThread().getName());
        TimeUnit.SECONDS.sleep(2);
        return new AsyncResult<>(100);
    }
}

3. 编写junit单元测试

接下来我们就编写一个单元测试类,对上面编写好的业务代码进行调用测试。

@SpringBootTest
@Slf4j
public class AsyncServiceTests {

    @Autowired
    private AsyncService asyncService;

    
    @Test
    public void test01() throws InterruptedException {
        log.info("调用开始,当前线程是 {}",Thread.currentThread().getName());
        asyncService.asyncProcess();
        Thread.sleep(100);
        log.info("调用结束,当前线程是 {}",Thread.currentThread().getName());
    }

    
    @Test
    public void test02() throws InterruptedException, ExecutionException {
        log.info("调用开始,当前线程是  {}",Thread.currentThread().getName());
        Future integerFuture = asyncService.asyncProcessHasReturn();
        Integer integer = integerFuture.get();
        System.out.println(integer);
        log.info("调用结束,当前线程是 {}",Thread.currentThread().getName());
    }
}

我们可以分别执行两个测试用例。

无返回值的测试用例的测试结果:

有返回值的测试用例的测试结果:

三. 程序优化

从上面的测试结果中我们不难看出,在Spring下我们要使用线程池其实非常的简单,只需在要异步调用的方法上加上@Async注解即可。以后调用该方法时,Spring就会自动使用默认的线程池,创建一个线程异步调用该方法。

但这样做会不会有什么问题呢?有没有我们需要优化的点呢?

答案是肯定的!Spring默认使用的Executor bean,和我们使用JDK中的Executors创建的线程池一样,都存在着阻塞队列的长度过长,可能会堆积大量的请求,从而导致OOM的问题。所以为了规避资源耗尽,我们一般会自己配置Executor bean(具体描述参照阿里巴巴开发规范1.4版),具体优化代码如下:

@Configuration
@Slf4j
public class AsyncConfig implements AsyncConfigurer {

    @Override
    @Bean("cs2105ThreadPool")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        // 设置核心线程数
        executor.setCorePoolSize(10);
        // 设置最大线程数
        executor.setMaxPoolSize(20);
        // 设置阻塞队列的容量
        executor.setQueueCapacity(20);
        // 除了核心线程数以外的线程的存货时间
        executor.setKeepAliveSeconds(60);
        // 设置线程池的线程的名称的前缀
        executor.setThreadNamePrefix("cs2105_");
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        // 拒绝策略
        executor.setRejectedExecutionHandler(
                new ThreadPoolExecutor.AbortPolicy()
        );
        executor.initialize();
        return executor;
    }

配置了自定义的Executor bean 之后,我们再次运行测试case,发现运行结果如下:

四. 结语

线程池在Java中一直都是一个非常重要的知识点,我们在面试中也会经常遇到这方面的题,希望大家好好掌握这里面的基础知识。如果还有什么问题,可以在评论区给留言哦!或者java学习+资料获取 加q群:691533824 或者扫码学习啦!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/959228.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号