Z次元
文章 笔记 日志
专题
专栏分类 文章归档
友链
友情链接 朋友圈
留言
头像
系列文章
Async/EnableAsync注解异步方法的使用
系列文章
知识积累
最后更新:2024/12/15|创建时间:2022/9/16
文章摘要
@Async 注解是 Spring 提供的一个公共线程池, 需要配合 @EnableAsync 注解开启。使用 @Async 注解的方法称之为异步方法, 相当于为该方法开了一个新的线程, 使其在不影响主线程的前提下运行。本文介绍了 @Async 注解的简单使用、自定义线程池以及重写配置等内容。

前言

@Async注解为spring提供的一个公共线程池,需要配合@EnableAsync注解开启。
使用@Async注解的方法称之为异步方法,相当于为该方法开了一个新的线程,使其在不影响主线程的前提下运行。

简单使用

使用@EnableAsync配置异步功能

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;

@Configuration
@EnableAsync
public class AsyncConfig {


}

服务层:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncTestService {

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


    /**
     * 异步方法
     * @throws InterruptedException
     */
    @Async
    public void method2() throws InterruptedException {
        logger.info("---------method2开始--------------");
        Thread.sleep(3000);
        logger.info("---------end2--------------");
    }
  
    /**
     * 异步方法
     * @throws InterruptedException
     */
    @Async
    public void method3() throws InterruptedException {
        logger.info("---------method3开始--------------");
        Thread.sleep(3000);
        logger.info("---------end3--------------");
    }

    /**
     * 非异步方法
     * @throws InterruptedException
     */
    public void method1() throws InterruptedException {
        logger.info("---------------------非异步方法开始-------------------");
        Thread.sleep(7777);
        logger.info("----------------------非异步方法开始结束--------------------");
    }

}


测试:


import com.example.demo.service.AsyncTestService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class WebController {

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

    @Autowired
    AsyncTestService asyncTestService;


    @RequestMapping("ahzoo")
    @ResponseBody
    public void toTest() throws InterruptedException {
        logger.info("----------主线程开始------------");
        long start = System.currentTimeMillis();
        asyncTestService.method1();
        asyncTestService.method2();
        asyncTestService.method3();
        Thread.sleep(3000);
        logger.info("--------------主线程结束,执行用时:" + (System.currentTimeMillis() - start) + "-------------ahzoo.cn------------------");
    }
}

注意观察日志打印时间,可以看到主线程的非异步方法先执行完毕,然后主线程中的其他异步方法在不影响主线程的前提下开始执行。最终主线程的用时约等于自身的3秒加非异步方法的7秒多。
图片

自定义

多数情况下,@Async注解的默认配置就已经足够。

自定义线程池

除了使用@Async提供的默认线程池外,我们还可以使用自定义线程池(推荐使用spring提供的线程池:ThreadPoolTaskExecutor),然后指定使用的线程池即可:@Async("自定义的线程池名").
示例:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableAsync
public class AsyncExecutorConfigurer {
  
    // corePoolSize: 定义核心线程数为 3
    private static final Integer CORE_POOL_SIZE = 3;
    // maximumPoolSize:定义最大线程数为 9
    private static final Integer MAX_POOL_SIZE = 9;
    // workQueue:定义任务队列为 ArrayBlockingQueue,并且容量为 90
    private static final Integer QUEUE_CAPACITY = 90;
    // keepAliveTime: 定义等待时间为 1000ms
    private static final Integer KEEP_ALIVE_TIME = 1000;
    // 线程池名称前缀
    private static final String THREAD_NAME = "Ahzoo-";

    @Bean
    public Executor getCustomizeAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(CORE_POOL_SIZE);
        executor.setMaxPoolSize(MAX_POOL_SIZE);
        executor.setQueueCapacity(QUEUE_CAPACITY);
        executor.setThreadNamePrefix(THREAD_NAME);
        executor.setKeepAliveSeconds(KEEP_ALIVE_TIME);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  
        // 初始化线程池(ThreadPoolTaskExecutor线程池需要进行初始化)
        executor.initialize();
  
        return executor;
    }
}

使用:
服务层:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncTestService {

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


    /**
     * 异步方法
     * @throws InterruptedException
     */
    @Async
    public void method2() throws InterruptedException {
        logger.info("---------method2开始--------------");
        Thread.sleep(3000);
        logger.info("---------end2--------------");
    }

    /**
     * 异步方法
     * @throws InterruptedException
     */
    @Async
    public void method3() throws InterruptedException {
        logger.info("---------method3开始--------------");
        Thread.sleep(3000);
        logger.info("---------end3--------------");
    }

    /**
     * 自定义异步方法
     * @throws InterruptedException
     */
    @Async("getCustomizeAsyncExecutor")
    public void method4() throws InterruptedException{
        logger.info("---------method4开始--------------");
        Thread.sleep(7000);
        logger.info("---------end4--------------");
    }


    /**
     * 自定义异步方法
     * @throws InterruptedException
     */
    @Async("getCustomizeAsyncExecutor")
    public void method5() throws InterruptedException{
        logger.info("---------method5开始--------------");
        Thread.sleep(9000);
        logger.info("---------end5--------------");
    }


    /**
     * 非异步方法
     * @throws InterruptedException
     */
    public void method1() throws InterruptedException {
        logger.info("---------------------非异步方法开始-------------------");
        Thread.sleep(7777);
        logger.info("----------------------非异步方法开始结束--------------------");
    }

}

测试:


import com.example.demo.service.AsyncTestService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class WebController {

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

    @Autowired
    AsyncTestService asyncTestService;

    @RequestMapping("ahzoo")
    @ResponseBody
    public void toTest() throws InterruptedException {
        logger.info("----------主线程开始------------");
        long start = System.currentTimeMillis();
        asyncTestService.method1();
        asyncTestService.method2();
        asyncTestService.method3();
        asyncTestService.method4();
        asyncTestService.method5();
        Thread.sleep(3000);
        logger.info("--------------主线程结束,执行用时:" + (System.currentTimeMillis() - start) + "-------------ahzoo.cn------------------");
    }

}

同样注意观察日志打印时间,可以看到除了非异步方法,各个异步方法之间都是独立异步执行,且不影响主线程。
同时,由于只配置了三个线程池,所以第四个使用@Async注解的方法并没有立即执行。
图片

重写配置

打开@Async注解的配置接口AsyncConfigurer。

public interface AsyncConfigurer {

    /**
    * 获取异步线程
    */
    @Nullable
    default Executor getAsyncExecutor() {
    	// 返回值是一个线程池,但是默认返回的是null,所以实现AsyncConfigurer接口后,只需要在这里返回自定义的线程池即可
        return null;
    }

    /**
	* 这个是一个异常处理器,如果要自定义的话,可以重新此方法
    */
    @Nullable
    default AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        // logger.error("ahzoo");
        return null;
    }
}

如果自定义配置的话,就实现AsyncConfigurer类,然后重写方法即可。
线程池的创建方式可以参照上面自定义中的线程池创建方式。
使用方式就是默认的使用方式,直接使用@Async注解即可。

版权声明
本文依据 CC-BY-NC-SA 4.0 许可协议授权,请您在转载时注明文章来源为 Z次元 ,若本文涉及转载第三方内容,请您一同注明。
更多专栏文章推荐
知识积累
人非生而知之者,孰能无惑?惑而不从师,其为惑也,终不解矣。
VO、DTO、BO、PO、DO区别
2022/6/28
MyBatis多表联查
2022/6/26
SpringBoot集成轻量级搜索引擎——Meilisearch
2025/8/7
JDK新版特性(18-21)
2025/2/7
评论区

删除确认

评论删除后无法恢复,请确认是否继续?
发表评论
删除 编辑 回复
腾

1

腾讯云开发者社区2022年10月20日

您好~我是腾讯云开发者社区运营,关注了您分享的技术文章,觉得内容很棒,我们诚挚邀请您加入腾讯云自媒体分享计划。完整福利和申请地址请见:https://cloud.tencent.com/developer/support-plan
作者申请此计划后将作者的文章进行搬迁同步到社区的专栏下,你只需要简单填写一下表单申请即可,我们会给作者提供包括流量、云服务器等,另外还有些周边礼物。|´・ω・)ノ

前言
简单使用
自定义
自定义线程池
重写配置
目录
前言
简单使用
自定义
自定义线程池
重写配置
博客
文章 笔记 日志
专题
专栏分类 文章归档
友链
友情链接 朋友圈
交流
留言 关于我
主页
菜单
置顶
主题
我的
十玖八柒
每天进步多一点
欢迎到访φ(゜▽゜*)♪
最新评论
个人占星:
DeepSeek没有想象中的好用
个人占星:
想给自己的网站弄个统计功能,但不会弄,头疼
永恒末匕:
好哇塞,这个厉害
十玖八柒:
测试图片发送
我的
关于我
个人主页
站点地图
RSS订阅
导航
十年之约
虫洞穿梭
开源博客
前端开源仓库
后端开源仓库
©2020 - 2025 By 十玖八柒 版权所有
豫ICP备20021466号