In Practice:一個由CountDownLatch引發的Bug

最近恢復Venus的工作,恰逢十一假期尾聲,突發一個Bug,讓人匪夷所依。我們今天快速倒序追蹤,層層回顧。

1. 背景

Venus交易中需要並行開數十至百個線程分別快速計算,程序中使用CountDownLatch用於計數,因爲之後後做其他處理分析,需要等待所有交易結束。

程序運行穩定正常,線上也已經0.16版本(個人線上),恰逢十一假期突然出問題,CountDownLatch無法歸零,導致整個主線程Hang在那裏。如何破?

2. 場景分析

我們直接看一下源代碼如何:

平鋪直敘,代碼中定義了CountDownLatch, 設定其數量爲股票個數,然後線程中進行countDown.

平時運行正常不過,十一爲何出問題?難道Venus想讓筆者家裏加班?

3. CountDownLanch

查看log問題主要出現在最終CountDownLatch無法歸零,按說我們都把countDown放到finally裏面了,應該不會出什麼問題了。

Anyway,我們先看一下CountDownLatch吧。

API中當調用await時候,調用線程處於等待掛起狀態,直至count變成0再繼續。

其大體原理如下:

Java官網中給出的事例代碼如下:

與我們的類似,除了多了個開始計時器,這個應該不是問題吧?官網如下的例子也說明了我們的猜測。

4. 深入研究

我們的焦點目光轉移至await。

await

public booleanawait(long timeout,TimeUnitunit)

throwsInterruptedException

使當前線程在鎖存器倒計數至零之前一直等待,除非線程被中斷或超出了指定的等待時間。如果當前計數爲零,則此方法立刻返回true值。

如果當前計數大於零,則出於線程調度目的,將禁用當前線程,且在發生以下三種情況之一前,該線程將一直處於休眠狀態:

由於調用countDown()方法,計數到達零;或者

其他某個線程中斷當前線程;或者

已超出指定的等待時間。

上述API詳細描述,有幾個疑點值得進一步推敲,首先我們的count並未到達0,所以應該是線程調用中出現了問題,難道是有一些沒有捕獲的異常?可是我們是在finally做的countdown啊?

把所有捕獲異常都換成Thowable, 無用!

網上有人云:

Executors.newFixedThreadPool這是個有固定活動線程數。當提交到池中的任務數大於固定活動線程數時,任務就會放到阻塞隊列中等待。

主任務和子任務都放到了線程池中,就有下面的情況發生,

主任務調用一次CountDownLatch實例的await()方法時,當前線程就會一直佔用一個活動線程,如果多次調用,那麼就會一直佔用多個活動線程,如果調用次數大於固定活動線程數,那麼就可能造成阻塞隊列中某些子任務一直不被執行,CountDownLatch實例的countDown()的方法一直不被調用,那麼對應的主任務所在線程就會無限等待,與死鎖現像一樣

我們的newFixedThreadPool是大了點,直接50-100多個,而我的mac只有2核(可憐啊),跟這個有關係麼?

我們把FixedThreadPool改成了默認cpu的核數,或者hard code成2,運行,無效,hang,再說以前也是50-100個,運行沒有問題啊。

解決辦法是最好不要用CountDownLatch實例的await(),歸避長時間阻塞線程的風險,任何多線程應用程序都有死鎖風險,改用CountDownLatch實例的await(long timeout, TimeUnit unit),設定超時時間,如果超時,

多線程程序確實有死鎖風險, 當然安全起見可以給定超時時間。回到我們這裏不解決問題啊,到底問題出在哪裏?

5. 真相大白

問題到這裏,我們把該懷疑的地方都懷疑了一遍,還是不得要領。難道問題出在某一股票交易中?恰逢十一假期,有關連?

好吧,只好使出大招,就是二分排查法,哈哈。說白了就是笨辦法,逐一排查,股票由100個到50十個,到40,到20,正常。難道真是跟線程數量有關係?

不科學啊。

反過來,把剩下的80個來運行一下,居然正常通過。

嗯,到此,感覺就快找到問題了。問題就出現在那20個股票當中的某些有問題。繼續排查,終於定位到600188,這麼好的代碼,有問題?

好吧,至此可以進入debug了。

終於的終於,問題定位到該股票在十一前恰好處罰交易策略,而逢十一假期,代碼中一段計算nextTradingDate的算法有點問題(其實是粘貼錯了變量),直接導致死循環!!!

好吧,那個高手沒有寫過死循環啊?

一秒鐘改掉後,Venus瞬間觸發100個線程,愉悅的開始交易!

總結

此問題雖然最終並非有併發導致的問題,而然其隱藏之深也與併發分不開;併發多線程是好東西,當然也要慎用,做到知其然知其所以然。

公衆號:技術極客TechBooster

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章