首页 > Java > java教程 > 正文

java中@Async异步调用的方法

PHPz
发布: 2023-05-05 19:10:04
转载
1759 人浏览过

    前言

    异步调用与同步调用

    • 同步调用:顺序执行,通过调用返回结果再次执行下一个调用

    • 异步调用:通过调用,无需等待返回结果,执行下一个调用

    1. @Async讲解

    其@Async的注解代码如下:

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Async {
        String value() default "";
    }
    登录后复制

    注解可以使用在类型以及方法中
    通过value定义其值,默认是空

    一般这个注解需要配合@EnableAsync,起源码如下

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import({AsyncConfigurationSelector.class})
    public @interface EnableAsync {
        Class<? extends Annotation> annotation() default Annotation.class;
    
        boolean proxyTargetClass() default false;
    
        AdviceMode mode() default AdviceMode.PROXY;
    
        int order() default Integer.MAX_VALUE;
    }
    登录后复制

    主要通过该注解放置在启动类中进行配置启动

    在启动类中添加如下:

    @SpringbootApplication
    @EnableAsync
    public class Application{
        public static void main(String[] args){
            SrpingApplication.run(Application.class, args);
        }
    }
    登录后复制
    登录后复制

    2. 用法

    2.1 同步调用

    从调用到返回函数结果才能执行下一步,称为同步调用

    service层 代码:

    public class Service{
        public void test01() throws InterruptedException{
            Thread.sleep(5000);
            System.out.println("保存日志");
        }
    }
    登录后复制

    控制层代码模块:

    public class Controler{
        @Autowired
        private Service service;
    
        @GetMapping("/test")
        public String getTest(){
            try{
                System.out.println("开始");
                service.test01();
                System.out.println("结束");            
            }catch(InterruptedException e){
                e.prinStackTrace();
            }
        }
        
    }
    登录后复制
    登录后复制

    通过springboot的启动类启动之后

    输出如下:

    开始

    // 此为等待5秒钟,终端不显示也不关闭

    结束

    2.2 异步调用

    异步调用,执行函数不用等返回结果就可以执行下一步

    service层 代码:
    主要是添加了@Async注解标识这个方法

    public class Service{
        @Async
        public void test01() throws InterruptedException{
            Thread.sleep(500);
            System.out.println("保存日志");
        }
    }
    登录后复制

    控制层代码模块:

    通过调用service层函数

    public class Controler{
        @Autowired
        private Service service;
    
        @GetMapping("/test")
        public String getTest(){
            try{
                System.out.println("开始");
                service.test01();
                System.out.println("结束");            
            }catch(InterruptedException e){
                e.prinStackTrace();
            }
        }
        
    }
    登录后复制
    登录后复制

    以及在启动类中加入注解启动 @EnableAsync

    @SpringbootApplication
    @EnableAsync
    public class Application{
        public static void main(String[] args){
            SrpingApplication.run(Application.class, args);
        }
    }
    登录后复制
    登录后复制

    3. 自定义线程池

    对于线程池的一些基本知识可看我之前的文章:

    java如何正确关闭线程以及线程池(代码实践含源码分析)
    java线程池的创建方式详细分析(全)

    如果不指定线程池,默认使用的线程池为SimpleAsyncTaskExecutor(来一个任务就创建一个线程,不断创建线程导致CPU过高引发OOM),自带的线程池一般都有弊端,一般推荐使用ThreadPoolExecutor(明确线程池的资源,规避风险)

    具体如下:

    • newFixedThreadPool:定死了线程数,任务队列还是无界的,(最大线程数只有队列满了,最大线程数才会创建),所以会造成OOM

    • newCachedThreadPool:没有设置最大线程数上限,创建大量的线程容易卡顿或者直接OOM

    通过自定义线程池可以调整线程池的配置,更好的资源利用

    @Async这个注解查找 AsyncConfigurer接口(实现类为AsyncConfigurerSupport,默认配置和方法都是空),所以可重写接口指定线程池。

    • 通过实现接口AsyncConfigurer

    • 继承AsyncConfigurerSupport

    • 自定义TaskExecutor(替代内置任务执行器)

    第三种方法:

    在application.xml中定义线程池的一些变量

    thread.core.size=16
    thread.max.size=16
    thread.queue.size=30
    
    thread.prefix=xx-
    登录后复制

    自定义线程池如下

    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    import java.util.concurrent.ThreadPoolExecutor;
    
    @Configuration
    public class ThreadPoolConfig {
        // 线程名称前缀
        @Value("${thread.prefix}")
        private String threadPrefix;
        
        // 核心线程数
        @Value("${thread.core.size}")
        private int coreSize;
    
        // 最大线程数
        @Value("${thread.max.size}")
        private int maxSize;
        
        // 队列长度
        @Value("${thread.queue.size}")
        private int queueSize;
        
        // 通过bean注解注入
        @Bean("xx")
        public ThreadPoolTaskExecutor taskExecutor() {
            ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
            //设置线程池参数信息
            taskExecutor.setCorePoolSize(coreSize);
            taskExecutor.setMaxPoolSize(maxSize);
            taskExecutor.setQueueCapacity(queueSize);
            taskExecutor.setThreadNamePrefix(threadPrefix);
            taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
            taskExecutor.setAwaitTerminationSeconds(30);
            
            //修改拒绝策略为使用当前线程执行
            taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            
            //初始化线程池
            taskExecutor.initialize();
            return taskExecutor;
        }
    }
    登录后复制

    以上是java中@Async异步调用的方法的详细内容。更多信息请关注PHP中文网其他相关文章!

    相关标签:
    来源:yisu.com
    本站声明
    本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
    热门教程
    更多>
    最新下载
    更多>
    网站特效
    网站源码
    网站素材
    前端模板
    关于我们 免责声明 Sitemap
    PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!