java异步任务
再见丶孙悟空 人气:1前言
最近在开发中遇到一个埋点的需求,考虑到不能影响原有业务逻辑,于是准备写一个异步任务去异步执行,但在 debug 的过程中却发现了一个奇怪的问题。
一、什么是异步任务
无论是生活中还是程序里,大体可以分为两种 : 同步和异步。
同步:比如你去吃海底捞,你要先点锅底,再点菜,然后服务员上锅底,再上菜,最后你才能吃上菜,这一过程得按顺序来。
异步任务:还是去吃海底捞,吃的人很多,你前面有很多人,你可能要排队,等到排到你才能再进入餐厅。但是如果中途你想去个厕所,怎么办,回来还得重新排队。于是就有了叫号,你先排队取号,然后你可以去按个摩,看个电影,做个 spa ,买杯奶茶 .... 。终于到你了,这时候会 通知 你排到了,然后你就能进去了。这个过程便是异步的。
二、SpringBoot + Async
一开始想着开一个线程池,把任务丢线程池里去完成。
后来想起来 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 是我们常用的功能,但是却不知道它真正的原理。日后遇到问题,要多从它的原理考虑。
加载全部内容