線程/併發問題是衆所周知難以複製的 - 這就是爲什麼您應該設計避免或至少將概率降至最低的原因之一。這就是不變對象如此寶貴的原因。嘗試將可變對象分離到單個線程,然後仔細控制線程之間的可變對象交換。嘗試使用對象移交的設計進行編程,而不是「共享」對象。對於後者,使用完全同步的控制對象(這些對象更容易推理),並避免讓同步對象利用其他必須同步的對象 - 也就是儘量保持它們獨立。你最好的防守是一個很好的設計。
死鎖是最容易調試的,如果在死鎖時可以獲得堆棧跟蹤。鑑於痕跡,其中大部分進行死鎖檢測,很容易找出原因,然後推理代碼爲什麼以及如何修復它。隨着死鎖,它總是會以不同的順序獲取相同的鎖。
活鎖更難 - 在錯誤狀態下能夠觀察系統是您最好的選擇。
種族條件往往是非常難以複製,更難以從手動代碼審查識別。有了這些,我通常會採用的路徑,除了大量的複製測試之外,還是要推斷可能性,並嘗試記錄信息來證明或反駁理論。如果你有直接的國家腐敗證據,你可以根據腐敗來推斷可能的原因。
系統越複雜,找到併發錯誤就越困難,並推斷它的行爲。利用JVisualVM和遠程連接分析器等工具 - 如果您可以連接到處於錯誤狀態的系統並檢查線程和對象,它們可以成爲救命稻草。
此外,請注意可能的行爲差異,這些差異取決於CPU內核數量,流水線數量,總線帶寬等。硬件變化可能會影響您複製問題的能力。一些問題只會出現在單核CPU上,其他只在多核上纔會出現。
最後一件事,嘗試使用與系統庫一起發佈的併發對象 - 例如Java java.util.concurrent
是你的朋友。編寫自己的併發控制對象很困難,充滿危險;如果您有選擇,請留給專家。
有些工具可以檢測競爭條件。對於.NET,您可以查看Microsoft的[CHESS](http://research.microsoft.com/en-us/projects/chess/),它會嘗試通過針對每種可能的交錯運行代碼來檢測競爭狀況。對於Java,您可以獲得[ThreadSafe](http://www.contemplateltd.com/threadsafe),該工具專門用於檢測和調試併發錯誤。 – 2014-03-18 11:42:31
FindBugs也是一個很好的靜態分析工具,用於查找潛在的線程錯誤。 – 2014-03-18 16:25:11