当前位置: 首页 > article >正文

Spring的异步详解(@Async)

1 引言

在java中异步线程很重要,比如在业务流处理时,需要通知硬件设备,发短信通知用户,或者需要上传一些图片资源到其他服务器这种耗时的操作,在主线程里处理会阻塞整理流程,而且我们也不需要等待处理结果之后再进行下一步操作,这时候就可以使用异步线程进行处理,这样主线程不会因为这些耗时的操作而阻塞,保证主线程的流程可以正常进行。

2 异步说明和原理

使用地方说明:

在方法上使用该@Async注解,申明该方法是一个异步任务;
在类上面使用该@Async注解,申明该类中的所有方法都是异步任务;
使用此注解的方法的类对象,必须是spring管理下的bean对象;
要想使用异步任务,需要在调用方法的类上配置@EnableAsync注解;

@Async的原理概括:

@Async的原理是通过 Spring AOP 动态代理 的方式来实现的。
在线程调用@Async注解标注的方法时,会调用代理,执行切入点处理器invoke方法,将方法的执行提交给线程池中的另外一个线程来处理,从而实现异步执行。
所以,相同类中的方法调用带@Async的方法是无法异步的,这种情况仍然是同步。

3 @Async的使用

3.1 调用方法的类上配置@EnableAsync注解

@Slf4j
@Service
@EnableAsync
public class DataAllServiceImpl implements DataAllService {

	@Autowired
	private DataSyncService dataSyncService;

	/**
	 * 同步所有数据
	 */
	@Override
	public void syncAll() {
		dataSyncService.syncPerson();
	}
}

3.2 方法或者类上加@Async注解

@Async
public class DataSyncServiceImpl implements DataSyncService {

	@Override
	public void syncPerson(){
		XxlJobHelper.log("开始采集Person表");
	}

4异步线程池

4.1 Spring默认的线程池的默认配置

 1.默认核心线程数:8,
 2. 最大线程数:Integet.MAX_VALUE,
 3. 队列使用LinkedBlockingQueue,
 4. 容量是:Integet.MAX_VALUE,
 5. 空闲线程保留时间:60s,
 6. 线程池拒绝策略:AbortPolicy

缺点:从最大线程数的配置上,相信看到问题:并发情况下,会无限创建线程

默认线程池的上述缺陷如何解决:答案是,自定义配置参数就可以了

4.2在配置文件中配置

spring:
  task:
    execution:
      pool:
        max-size: 6
        core-size: 3
        keep-alive: 3s
        queue-capacity: 1000
        thread-name-prefix: name

4.3 自定义线程池

编写配置类

@Configuration
@Data
public class ExecutorConfig{
    //核心线程
    private int corePoolSize;
    //最大线程
    private int maxPoolSize;
    //队列容量
    private int queueCapacity;
    //保持时间
    private int keepAliveSeconds;
    //名称前缀
    private String preFix;
 
    @Bean("MyExecutor")
    public Executor myExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setKeepAliveSeconds(keepAliveSeconds);
        executor.setThreadNamePrefix(preFix);
        executor.setRejectedExecutionHandler( new ThreadPoolExecutor.AbortPolicy());
        executor.initialize();
        return executor;
    }
}

使用自定义线程池

@Component
public class MyAsyncTask {
     @Async("MyExecutor") //使用自定义的线程池(执行器)
    public void asyncCpsItemImportTask(Long platformId, String jsonList){
        //...具体业务逻辑
    }
}

5 异步中的事务和返回

5.1异步中的事务

在@Async标注的方法,同时也使用@Transactional进行标注;在其调用数据库操作之时,将无法产生事务管理的控制,原因就在于其是基于异步处理的操作。
那该如何给这些操作添加事务管理呢?
可以将需要事务管理操作的方法放置到异步方法内部,在内部被调用的方法上添加@Transactional

5.2 异步返回

异步的业务逻辑处理场景 有两种:一个是不需要返回结果,另一种是需要接收返回结果。不需要返回结果的比较简单,就不多说了。
需要接收返回结果的示例如下

@Async("MyExecutor")
public Future<Map<Long, List>> queryMap(List ids) {
    List<> result = businessService.queryMap(ids);
    ..............
    Map<Long, List> resultMap = Maps.newHashMap();
    ...
    return new AsyncResult<>(resultMap);
}

6 失效情况分析

6.1 未使用@EnableAsync注解

在Spring中要开启@Async注解异步的功能,需要在项目的启动类,或者配置类上,或者调用方法的类上使用@EnableAsync注解。

6.2 内部方法调用

我们在日常开发中,经常需要在一个方法中调用另外一个方法。例如:

@Slf4j
@Service
public class UserService {

    public void test() {
        async("test");
    }

    @Async
    public void async(String value) {
        log.info("async:{}", value);
    }
}

6.3 方法非public

因为private修饰的方法,只能在UserService类的对象中使用。
而@Async注解的异步功能,需要使用Spring的AOP生成UserService类的代理对象,该代理对象没法访问UserService类的private方法,因此会出现@Async注解失效的问题。

6.4 方法返回值错误

想要使用@Async注解的异步功能,相关方法的返回值必须是void或者Future。

6.5 方法用static修饰了

使用@Async注解声明的方法,必须是能被重写的,很显然static修饰的方法,是类的静态方法,是不允许被重写的。
因此这种情况下,@Async注解的异步功能会失效。

6.6 方法用final修饰

在Java种final关键字,是一个非常特别的存在。
用final修饰的类,没法被继承。
用final修饰的方法,没法被重写。
用final修饰的变量,没法被修改。
如果final使用不当,也会导致@Async注解的异步功能失效

6.7 业务类没加@Service@controller@component等注解

业务类需要交给Spring管理,通过AOP生成代理类,因此需要加上以上注解。

参考:https://jingzh.blog.csdn.net/article/details/129769200?fromshare=blogdetail&sharetype=blogdetail&sharerId=129769200&sharerefer=PC&sharesource=weixin_43228381&sharefrom=from_link


http://www.kler.cn/a/386449.html

相关文章:

  • 信号-3-信号处理
  • 设计模式练习(一) 单例模式
  • 虚幻引擎 CEO 谈元宇宙:发展、策略与布局
  • 爱普生SG-8200CJ可编程晶振在通信设备中的应用
  • 半导体企业如何利用 Jira 应对复杂商业变局?
  • 【Golang】Channel的ring buffer实现
  • arkUI:层叠布局(Stack)
  • 测试概念以及测试bug
  • cannot locate symbol _ZTVNSt6__ndk119basic_ostringstreamIcNS_
  • 自动化细胞核分割与特征分析
  • 如何利用动态住宅IP高效抓取亚马逊数据并避开封禁
  • react的创建与书写
  • node.js安装配置(Windows)
  • 我应该如何使用这个API接口来展示商品信息呢
  • 【图像与点云融合教程(五)】海康相机 ROS2 多机分布式实时通信功能包
  • 美的品牌店铺运营全解析:洞察用户行为驱动增长
  • 【excel基本操作-sumif绝对引用和相对引用
  • 《Atomic Picnic》进不去游戏解决方法
  • 呼叫中心外呼主要用于哪些场景?
  • 【ARM Linux 系统稳定性分析入门及渐进 1.9.1 -- Crash 命令 System State 集合】
  • 关于99.9% 达成读码率方案
  • js 下载在线视频等多个文件到一个文件夹导出压缩包下载到本地
  • 棱镜七彩参加“融易行”产融对接南京站项目路演活动 展示供应链安全创新成果
  • 时序数据库之influxdb和倒排索引以及LSM-TREE
  • C++移动语义和lambda表达式
  • LINUX离线安装Milvus