Spring @Async异步方法中的线程隔离

Spring @Async异步方法中的线程隔离

目录

转载说明

https://www.cnblogs.com/didispace/p/15307355.html

内容摘要

上一篇分享中介绍了Spring中的异步方法的使用方法,文中提到,如果我们不显示创建线程池,异步方法会放到Spring默认提供的线程池SimpleAsyncTaskExecutor中执行, 但是需要注意的是,Spring提供的这个默认线程池的队列长度是没有限制的,也就是说,如果一直往默认线程池你提交任务有可能OOM的风险,所以一般情况都建议使用自己创建的线程池。

本文提到的线程隔离,则是定义多个线程池,它的使用场景是,如果是完全两类独立的业务,业务A比较耗时,另外一个业务B处理速度很快,如果使用同一个线程池,就会拉低业务B的任务处理效率。所以如果需要解决这个问题就需要让异步方法指派到不同的线程池,以便让不同的异步业务互相不影响。

代码实例

第一步:创建线程池

@EnableAsync
@Configuration
public class ThreadPoolConfig {

    @Bean
    public Executor taskExecutor1(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(2);
        executor.setQueueCapacity(10);
        executor.setKeepAliveSeconds(60);
        //用executor.setThreadNamePrefix设置了线程名的前缀
        executor.setThreadNamePrefix("executor-1-");
        // 如果线程池的线程数量达到上限,该策略会把任务队列中的任务放在调用者线程当中运行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }

    @Bean
    public Executor taskExecutor2(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(2);
        executor.setQueueCapacity(10);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("executor-2-");
        // 如果线程池的线程数量达到上限,该策略会把任务队列中的任务放在调用者线程当中运行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}

第二步:编写异步方法

这里@Async注解中定义的taskExecutor1taskExecutor2就是线程池的名字。由于在第一步中,我们没有具体写两个线程池Bean的名称,所以默认会使用方法名,也就是taskExecutor1taskExecutor2

@Service
@Slf4j
public class AsyncService {
    public static Random random = new Random();

    @Async("taskExecutor1")
    public CompletableFuture<String> doTaskOne(String taskNo) throws Exception{
        log.info("开始任务:{}", taskNo);
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务:{},耗时:{} 毫秒", taskNo, end - start);
        return CompletableFuture.completedFuture("任务完成");
    }

    @Async("taskExecutor2")
    public CompletableFuture<String> doTaskTwo(String taskNo) throws Exception {
        log.info("开始任务:{}", taskNo);
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务:{},耗时:{} 毫秒", taskNo, end - start);
        return CompletableFuture.completedFuture("任务完成");
    }
}

第三步:测试观察

在上面的单元测试中,一共启动了6个异步任务,前三个用的是线程池1,后三个用的是线程池2。

@SpringBootTest
class AsyncDemoApplicationTests {

    @Test
    void contextLoads() {
    }

    @Autowired
    AsyncService service;
    @Test
    public void test01() throws Exception{
        // 线程池1
        CompletableFuture<String> task1 = service.doTaskOne("1");
        CompletableFuture<String> task2 = service.doTaskOne("2");
        CompletableFuture<String> task3 = service.doTaskOne("3");

        // 线程池2
        CompletableFuture<String> task4 = service.doTaskTwo("4");
        CompletableFuture<String> task5 = service.doTaskTwo("5");
        CompletableFuture<String> task6 = service.doTaskTwo("6");

        // 一起执行
        CompletableFuture.allOf(task1, task2, task3, task4, task5, task6).join();
    }
}
INFO 5524 --- [   executor-1-1] c.l.a.asyncdemo.service.AsyncService     : 开始任务:1
INFO 5524 --- [   executor-2-2] c.l.a.asyncdemo.service.AsyncService     : 开始任务:5
INFO 5524 --- [   executor-1-2] c.l.a.asyncdemo.service.AsyncService     : 开始任务:2
INFO 5524 --- [   executor-2-1] c.l.a.asyncdemo.service.AsyncService     : 开始任务:4
INFO 5524 --- [   executor-2-1] c.l.a.asyncdemo.service.AsyncService     : 完成任务:4,耗时:614 毫秒
INFO 5524 --- [   executor-2-1] c.l.a.asyncdemo.service.AsyncService     : 开始任务:6
INFO 5524 --- [   executor-2-2] c.l.a.asyncdemo.service.AsyncService     : 完成任务:5,耗时:3188 毫秒
INFO 5524 --- [   executor-1-2] c.l.a.asyncdemo.service.AsyncService     : 完成任务:2,耗时:5314 毫秒
INFO 5524 --- [   executor-1-2] c.l.a.asyncdemo.service.AsyncService     : 开始任务:3
INFO 5524 --- [   executor-2-1] c.l.a.asyncdemo.service.AsyncService     : 完成任务:6,耗时:5539 毫秒
INFO 5524 --- [   executor-1-1] c.l.a.asyncdemo.service.AsyncService     : 完成任务:1,耗时:6622 毫秒
INFO 5524 --- [   executor-1-2] c.l.a.asyncdemo.service.AsyncService     : 完成任务:3,耗时:6136 毫秒
上一篇:批量弱口令升级版本


下一篇:简单的@Async使用