10人参与 • 2025-04-26 • Java
在 java 中,同步调用和异步调用是两种不同的操作方式,用于处理方法调用和任务执行。
(1)定义:同步调用指的是调用方法时,调用者会等待被调用的方法执行完成后才继续执行后续的代码。也就是说,方法调用是阻塞的,当前线程会被阻塞直到方法执行结束并返回结果。
(2)示例:
public class syncexample { public static void main(string[] args) { system.out.println("start"); //同步调用 string result = longrunningtask(); system.out.println("result: " + result); system.out.println("end"); } public static string longrunningtask() { try { //模拟长时间运行的任务 thread.sleep(3000); } catch (interruptedexception e) { e.printstacktrace(); } return "task completed"; } }
(3)解释:
在上述代码中,longrunningtask()
方法会暂停当前线程 3 秒钟,然后返回结果。在 main
方法中,longrunningtask()
的调用是同步的,system.out.println("result: " + result);
会等待 longrunningtask()
完成后才执行。
1.2.异步调用
(1)定义:异步调用指的是调用方法时,调用者不会等待方法执行完成,而是立即继续执行后续的代码。被调用的方法在后台线程中执行,调用者可以通过回调、future 模式或其他机制获取结果。
(2)示例:
import java.util.concurrent.completablefuture; public class asyncexample { public static void main(string[] args) { system.out.println("start"); //异步调用 completablefuture<string> future = completablefuture.supplyasync(() -> longrunningtask()); //继续执行其他操作 system.out.println("doing other work while waiting for result..."); //获取异步任务的结果(会阻塞,直到任务完成) future.thenaccept(result -> system.out.println("result: " + result)); system.out.println("end"); } public static string longrunningtask() { try { //模拟长时间运行的任务 thread.sleep(3000); } catch (interruptedexception e) { e.printstacktrace(); } return "task completed"; } }
(3)解释:在上述代码中,longrunningtask()
被异步执行,通过 completablefuture.supplyasync
方法。在异步执行期间,system.out.println("doing other work while waiting for result...");
会继续执行,不会被阻塞。future.thenaccept(result -> system.out.println("result: " + result));
会在任务完成后处理结果。
@async
注解中的 async 是单词 asynchronous
的缩写。asynchronous 指的是“异步的”,即操作或方法的执行不会阻塞当前线程,允许在后台执行任务的同时继续进行其他操作。使用 @async 注解可以让 spring 在后台线程池中异步执行被注解的方法,从而提升应用的性能和响应能力。@async
注解的代码如下所示:
package org.springframework.scheduling.annotation; import java.lang.annotation.documented; import java.lang.annotation.elementtype; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; import java.lang.annotation.target; import org.springframework.aot.hint.annotation.reflective; @target({elementtype.type, elementtype.method}) @retention(retentionpolicy.runtime) @documented @reflective public @interface async { string value() default ""; }
@async
注解用在方法和类上的作用分别如下:
(1)功能:标记方法为异步执行,这意味着方法将在一个新的线程中执行,不会阻塞调用该方法的线程。并且方法所在的类必须要被 spring 作为 bean 管理。
(2)限制:只对 public
方法有效,且方法返回类型为 void
或者 future
。
(3)例子:
@async public completablefuture<string> asyncmethod() { // 执行异步任务 }
(1)功能:标记类中的所有 public
方法都为异步执行。这个类必须要被 spring 作为 bean 管理,并且所有被 @async
注解的方法都会异步执行。
(2)限制:类上使用 @async 不一定能保证类中所有方法都异步执行,特别是如果方法被类内部直接调用时,例如方法 asyncmethod2
在同一类内直接调用方法 asyncmethod1
,asyncmethod1
会在当前线程中同步执行,而不是异步执行。
(3)例子:
@async @service public class myservice { public completablefuture<string> asyncmethod1() { // 执行异步任务 } public completablefuture<string> asyncmethod2() { // 执行异步任务 } }
一般来说,@async
注解用在方法上来指定特定方法异步执行,标记整个类的方式较少使用。
例如,在 spring boot 项目的启动类上增加 @enableasync 注解:
@enableasync //开启异步调用 @springbootapplication public class myexampleapplication { public static void main(string[] args) { springapplication.run(myexampleapplication.class, args); } }
@enableasync 注解用于启用 spring 框架的异步方法执行功能。它使得应用程序能够处理被 @async 注解标记的方法,确保这些方法在独立的线程中异步执行。将 @enableasync 注解添加到配置类或者启动类上后,spring 将自动配置所需的支持,并允许 @async 注解在应用程序中生效。
package com.example.myexample.service.async; import lombok.extern.slf4j.slf4j; import org.springframework.scheduling.annotation.async; import org.springframework.stereotype.service; @slf4j @service public class myasynctask { @async public void notifyuser() { log.info("asynctask 方法所处的线程:{}", thread.currentthread().getname()); //模拟处理异步任务 try { thread.sleep(3000); log.info("通知用户保存订单成功"); } catch (interruptedexception e) { throw new runtimeexception(e); } } }
package com.example.myexample.controller; import com.example.myexample.service.orderservice; import org.springframework.web.bind.annotation.postmapping; import org.springframework.web.bind.annotation.requestmapping; import org.springframework.web.bind.annotation.restcontroller; import javax.annotation.resource; @restcontroller @requestmapping("asynctest") public class asynctestcontroller { @resource private orderservice orderservice; @postmapping public string saveorder() { orderservice.saveorder(); return "success"; } } package com.example.myexample.service; public interface orderservice { void saveorder(); } package com.example.myexample.service.impl; import com.example.myexample.service.orderservice; import com.example.myexample.service.async.myasynctask; import lombok.extern.slf4j.slf4j; import org.springframework.stereotype.service; import javax.annotation.resource; @slf4j @service public class orderserviceimpl implements orderservice { @resource private myasynctask myasynctask; @override public void saveorder() { log.info("saveorder 方法所处的线程:{}", thread.currentthread().getname()); log.info("保存订单"); //调用异步方法 myasynctask.notifyuser(); postprocess(); } public void postprocess() { log.info("postprocess 方法所处的线程:{}", thread.currentthread().getname()); } }
上述代码只包括了调用异步方法的核心部分,其它部分代码(例如 controller 层)并未给出。
使用 postman 进行测试:
http://localhost:8080/asynctest/
日志打印结果如下,从中可以发现:方法 saveorder
和 notifyuser
在不同的线程中执行了,即 notifyuser
确实被异步调用了。
如果要将异步方法改为有返回值,可以将返回类型从 void
改为 future<t>
或 completablefuture<t>
。这里我们使用 completablefuture 作为返回类型:
@async public completablefuture<string> notifyuser2() { log.info("asynctask 方法所处的线程:{}", thread.currentthread().getname()); // 模拟处理异步任务 try { thread.sleep(3000); log.info("通知用户保存订单成功"); } catch (interruptedexception e) { throw new runtimeexception(e); } return completablefuture.completedfuture("通知用户保存订单成功"); } public void saveorder2() { log.info("saveorder 方法所处的线程:{}", thread.currentthread().getname()); log.info("保存订单"); completablefuture<string> future = myasynctask.notifyuser2(); //在需要时可以调用 get() 来阻塞当前线程直到异步任务完成 try { string result = future.get(); // 这将阻塞直到异步任务完成 system.out.println("任务结果: " + result); } catch (exception e) { e.printstacktrace(); } }
有关 future 模式的相关知识可以参考 java 并发编程面试题——future 模式这篇文章。
@async
注解在某些情况下可能会失效,包括:
@async
方法必须是 public
,并且不能是 final
、static
或 private
。@async
注解的类被 spring 扫描到。通常需要将类放在 @component
、@service
或其他 spring 管理的组件中。@enableasync
注解,spring 不会启用异步处理。@async
方法被同一类的其他方法调用时不会生效,因为 spring 的代理机制无法处理同一类内的调用。future
或 void
,确保返回值类型正确处理。需要注意的是,如果代码编写不当,在使用 @async
注解可能会导致循环依赖,例如:
@service public class cdtestsservice1impl implements cdtestsservice1 { @autowired private cdtestsservice2 cdtestsservice2; @async @override public void performasynctask() { system.out.println("performasynctask..."); } } @service public class cdtestsservice2impl implements cdtestsservice2 { @autowired private cdtestsservice1 cdtestsservice1; @override public void performtask() { cdtestsservice1.performasynctask(); } } @restcontroller @requestmapping("asynctest") public class asynctestcontroller { @resource private cdtestsservice2 cdtestsservice2; @postmapping("/cdtest") public string cdtest() { cdtestsservice2.performtask(); return "success"; } }
当启动项目时,会出现循环依赖的异常提示:
循环依赖:
@async 注解的影响:
(1)使用构造函数注入
使用构造函数注入可以避免循环依赖问题。构造函数注入在 bean 创建时确保所有依赖项都已解决,因此更可靠。
@service public class cdtestsservice1impl implements cdtestsservice1 { private final cdtestsservice2 cdtestsservice2; @autowired public cdtestsservice1impl(cdtestsservice2 cdtestsservice2) { this.cdtestsservice2 = cdtestsservice2; } @async @override public void performasynctask() { system.out.println("performasynctask..."); } } @service public class cdtestsservice2impl implements cdtestsservice2 { private final cdtestsservice1 cdtestsservice1; @autowired public cdtestsservice2impl(cdtestsservice1 cdtestsservice1) { this.cdtestsservice1 = cdtestsservice1; } @override public void performtask() { cdtestsservice1.performasynctask(); } }
(2)使用 @lazy 注解@lazy
注解可以推迟 bean 的初始化,解决循环依赖问题。你可以将 @lazy
注解添加到字段注入中,以便在需要时才加载依赖。
@service public class cdtestsservice1impl implements cdtestsservice1 { @autowired @lazy private cdtestsservice2 cdtestsservice2; @async @override public void performasynctask() { system.out.println("performasynctask..."); } } @service public class cdtestsservice2impl implements cdtestsservice2 { @autowired @lazy private cdtestsservice1 cdtestsservice1; @override public void performtask() { cdtestsservice1.performasynctask(); } }
(3)分离异步调用
如果 performasynctask
不需要立即执行,考虑将它移到另一个服务中,或通过其他机制避免直接的循环依赖。
@service public class cdtestsservice1impl implements cdtestsservice1 { @autowired private taskexecutor taskexecutor; // spring 提供的 taskexecutor @override public void performasynctask() { taskexecutor.execute(() -> { system.out.println("performasynctask..."); }); } } @service public class cdtestsservice2impl implements cdtestsservice2 { @autowired private cdtestsservice1 cdtestsservice1; @override public void performtask() { cdtestsservice1.performasynctask(); } }
使用 taskexecutor
来执行异步任务可以将异步执行的逻辑与服务依赖解耦,避免直接在服务之间形成循环依赖。
到此这篇关于spring 框架@async 注解的文章就介绍到这了,更多相关spring @async 注解内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论