发布时间:2023-09-14 10:00
最近在开发中遇到一个埋点的需求,考虑到不能影响原有业务逻辑,于是准备写一个异步任务去异步执行,但在 debug 的过程中却发现了一个奇怪的问题。
无论是生活中还是程序里,大体可以分为两种 : 同步和异步。
同步:比如你去吃海底捞,你要先点锅底,再点菜,然后服务员上锅底,再上菜,最后你才能吃上菜,这一过程得按顺序来。
异步任务:还是去吃海底捞,吃的人很多,你前面有很多人,你可能要排队,等到排到你才能再进入餐厅。但是如果中途你想去个厕所,怎么办,回来还得重新排队。于是就有了叫号,你先排队取号,然后你可以去按个摩,看个电影,做个 spa ,买杯奶茶 .... 。终于到你了,这时候会 通知 你排到了,然后你就能进去了。这个过程便是异步的。
一开始想着开一个线程池,把任务丢线程池里去完成。
后来想起来 SpringBoot 有一个比较方便的 异步 框架 Async
代码也很简单,只需要在需要异步执行的方法上加个 @Async ,SpringBoot 启动类上添加 @EnableAsync 即可
@Async public void task() { // do something }
代码虽然少,但是坑可不会随着代码量的减少而减少。
为了方便起见,我本地搭了个 demo,直接上代码
@RestController public class AsyncController { @Autowired private AsyncService asyncService; @GetMapping(\"/v1/say\") public String sayV1() { asyncService.sayV1(); return \"success1\"; } @GetMapping(\"/v2/say\") public String sayV2() { asyncService.sayV2(); return \"success2\"; } }
@Service public class AsyncService { public void sayV1() { try { Thread.sleep(3000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(\"hello world\"); } @Async public void sayV2() { try { Thread.sleep(3000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(\"hello world\"); } }
很简单的 demo,提供两个接口,/v1/say 和 /v2/say,一个同步执行,一个异步执行,通过 sleep 3 秒来模拟耗时的任务
正常启动,没有任何问题,同步执行的等 3 秒,主线程才会返回,异步执行的立刻返回,等3 秒才会输出 helloworld
但是,当我加上断点后,问题产生了。
我先是在 打印 hello world 那行加上个断点,效果和原来的一样,只是打印前被阻塞了,但并不影响主线程的返回。
编辑
但当我把断点加在方法进来的位置,发现 主线程居然被阻塞了!
编辑
各种问题排查,@Async 没有生效,异步任务等待主线程返回,都没有找到有效的解决方法。
后来经过一个同事提醒,会不会是 debug 功能阻塞的线程呢?
抱着试一试的态度,我找到了 debug 这边的配置
编辑
断点可以选择阻塞 jvm 或者是 阻塞当前线程,默认是阻塞 jvm。
将 suspend 选择 Thread,便不会再阻塞主线程了
我们都是站在巨人的肩膀上编程,很多事情都是只知其果,不知其因,debug 是我们常用的功能,但是却不知道它真正的原理。日后遇到问题,要多从它的原理考虑。
到此这篇关于一次java异步任务的文章就介绍到这了,更多相关java异步任务内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!