大多数时候处理业务都是以同步的方式来实现的。但在有些特殊的场景中,需要用异步的方式来实现。
1、最原始的实现异步的方式:单独起一个线程。缺点在于:异步处理业务太多时,同时运行的线程太多,可能导致服务器崩溃。
2、然后,出现了线程池,线程池对线程数量进行控制和对线程进行复用,解决了上面的问题。
3、在2中,程序员需要自己编写一个线程,然后交给线程池管理。spring为了简化程序员的代码,内置了@Async注解,程序员只需编写纯业务代码和设置自己的线程池即可。
基于@Async标注的⽅法,称之为异步⽅法;这些⽅法将在执⾏的时候,将会在独⽴的线程中被执⾏,调⽤者⽆需等待它的完成,即可继续其他的操作。
如何使用:
1、在应用中用@EnableAsync注解来启用@Aysnc注解。可以在spring⼊⼝类加,也可以在需要调⽤异步⽅法的类上加,也可以在应用配置类上加。
2、在需要异步的方法上加@Aysnc。如果加在类上,相当于这个类的所有方法都加了@Aysnc,那么这个类所有的⽅法都是异步执⾏的。
public class AysncTask { /** * taskA()被调用时,该方法会被异步执行:会将任务交给线程池去完成 */ @Async public void taskA() { System.out.println("AAAAAAAA"); } public void taskB() { System.out.println("BBBBBBBBBBB"); } }
@Async public class AysncTask { /** * taskA()被调用时,该方法会被异步执行:会将任务交给线程池去完成 */ public void taskA() { System.out.println("AAAAAAAA"); } /** * taskB()被调用时,该方法会被异步执行:会将任务交给线程池去完成 */ public void taskB() { System.out.println("BBBBBBBBBBB"); } }
3、如果在@Aysnc中没有指定线程池,会默认使用spring提供的默认线程池SimpleAsyncTaskExecutor。
缺点:线程池为每个任务都单独创建一个线程,不会重用线程。这样,就跟最原始实现异步的方式有了相同的缺点:异步处理业务太多时,同时运行的线程太多,可能导致服务器崩溃。
因此,我们一定要创建自己的线程池,并给异步任务指定到哪个线程池去执行。
创建线程池:
@Configuration public class ExecutorConfig { @Bean public Executor asyncServiceExecutor() { log.info("开始线程池:asyncServiceExecutor"); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //配置核心线程数 executor.setCorePoolSize(50); //配置最大线程数 executor.setMaxPoolSize(100); //配置队列大小 executor.setQueueCapacity(99999); //配置线程池中的线程的名称前缀 executor.setThreadNamePrefix("async-service-"); // 设置拒绝策略:当pool已经达到max size的时候,如何处理新任务 // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //执行初始化 executor.initialize(); return executor; } }
为任务指定线程池:
public class AysncTask { /** * taskA()被调用时,该方法会被异步执行:会将任务交给线程池去完成 */ @Async("asyncServiceExecutor") public void taskA() { System.out.println("AAAAAAAA"); } public void taskB() { System.out.println("BBBBBBBBBBB"); } }
使用过程中的其他注意点:
1、@Aysnc修饰的异步⽅法不能和调⽤此⽅法的方法放在⼀个类⾥⾯。
如:@Aysnc修改的方法A(),在方法B()里调用方法A()。B()和A()不能在同一个类里。
2、@Aysnc修饰的异步⽅法上加@Transactional将无法产生事务管理。因为异步方法单独开了一个线程导致的。
解决办法:将需要做事务管理的代码单独写成一个方法,加上@Transactional,然后在异步调用方法中调用这个方法。
3、可以为异步方法设置返回值,但必须是java.util.concurrent.Future类型,否则接收不到。如:Future的实现类:AsyncResult、CompletableFuture。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异