一次java異步任務的實戰記錄
前言
最近在開發中遇到一個埋點的需求,考慮到不能影響原有業務邏輯,於是準備寫一個異步任務去異步執行,但在 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 是我們常用的功能,但是卻不知道它真正的原理。日後遇到問題,要多從它的原理考慮。
到此這篇關於一次java異步任務的文章就介紹到這瞭,更多相關java異步任務內容請搜索LevelAH以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持LevelAH!
推薦閱讀:
- Springboot任務之異步任務的使用詳解
- 詳解Springboot中的異步、定時、郵件任務
- 一篇文章掌握Java Thread的類及其常見方法
- 詳解Java停止線程的四種方法
- Java Spring註解之@Async的基本用法和示例