关于java中@Async异步调用详细解析附代码

 更新时间:2022年07月14日 08:48:31   作者:码农研究僧  
本文主要介绍了java关于@Async异步调用详细解析附代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

异步调用与同步调用

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

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异步调用详细解析附代码的文章就介绍到这了,更多相关java @Async异步调用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 一文了解jJava中的加密与安全

    一文了解jJava中的加密与安全

    常见的编码有ASCII码、Unicode编码。最简单的编码是直接给每个字符指定一个若干字节表示的整数,复杂一点的编码就需要根据已有的编码推算出来。本文将为大家详细讲讲Java重点加密与安全,感兴趣的可以了解一下
    2022-07-07
  • Spring中BeanUtils.copyProperties的坑及解决

    Spring中BeanUtils.copyProperties的坑及解决

    这篇文章主要介绍了Spring中BeanUtils.copyProperties的坑及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • SpringBoot实现多租户架构

    SpringBoot实现多租户架构

    在SpringBoot中可以通过多数据源和动态路由来实现多租户机制,本文主要介绍了SpringBoot实现多租户架构,具有一定的参考价值,感兴趣的可以里哦啊接一下
    2024-03-03
  • 详解SpringBoot 解决拦截器注入Service为空问题

    详解SpringBoot 解决拦截器注入Service为空问题

    这篇文章主要介绍了详解SpringBoot 解决拦截器注入Service为空问题的解决,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • springboot与dubbo的版本匹配问题

    springboot与dubbo的版本匹配问题

    这篇文章主要介绍了springboot与dubbo的版本匹配问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • 使用springboot+druid双数据源动态配置操作

    使用springboot+druid双数据源动态配置操作

    这篇文章主要介绍了使用springboot+druid双数据源动态配置的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • SpringMVC请求乱码处理的2种方式

    SpringMVC请求乱码处理的2种方式

    这篇文章主要介绍了SpringMVC请求乱码处理的2种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • Java Web项目创建并实现前后端交互

    Java Web项目创建并实现前后端交互

    本文主要介绍了Java Web项目创建并实现前后端交互,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • 五个很实用的IDEA使用技巧分享

    五个很实用的IDEA使用技巧分享

    IntelliJ IDEA 是一款优秀的 Java 集成开发环境,它提供了许多强大的功能和快捷键,可以帮助开发者提高编码效率和质量,本文就在为你介绍博主常用的五个IntelliJ IDEA使用技巧,希望能够给你带来一些工作效率上的提升
    2023-10-10
  • Java使用jdbc连接MySQL数据库实例分析

    Java使用jdbc连接MySQL数据库实例分析

    这篇文章主要介绍了Java使用jdbc连接MySQL数据库,结合实例形式分析了Java基于jdbc链接mysql的相关配置及工具类的定义相关操作技巧,需要的朋友可以参考下
    2018-07-07

最新评论