Synchronized: 遞歸與死鎖

所謂遞歸函數就是自調用函數,在函數體內直接或間接的調用自己,即函數的嵌套是函數本身。 
遞歸方式有兩種:直接遞歸和間接遞歸,直接遞歸就是在函數中出現調用函數本身。間接遞歸,指函數中調用了其他函數,而該其他函數又調用了本函數。
那什麼時候使用遞歸呢?一般來說當你要在某段代碼邏輯中使用循環迭代的時候但是迭代的次數在迭代之前無法知曉的情況下使用遞歸。打個比方你要在一個文件夾中查找某個文件,而這個文件夾底下有N多子文件夾和文件,當你在不知道有多少層文件夾和文件的情況下你就得用到遞歸了。
遞歸的優點就是讓代碼顯得很簡潔,同時有些應用場景不得不使用遞歸比如前面說的找文件。遞歸是個好東西但是在某些時候也會給你帶來一些麻煩。比如在多線程的環境下使用遞歸,遇到了多線程那麼就不得不面對同步的問題。而遞歸程序遇到同步的時候很容易出問題。多線程的遞歸就是指遞歸鏈中的某個方法由另外一個線程來操作,以下代碼的意思都是這個意思即調用recursive()和businessLogic()並非一個線程(如果是在一個線程中就不存在死鎖問題,例如下面的recursive變成private就不存在問題。)

 

 

以上這段代碼就是個能形成死鎖的代碼,事實上這個“synchronized”放在“businessLogic()”和“recursive()”都會形成死鎖,並且是多線程的情況下就會鎖住!他的邏輯順序是先執行recursive()方法然後接下來執行businessLogic()方法同時將businessLogic()方法鎖住,接下來程序進入businessLogic()方法內部執行完打印語句後開始執行recursive(),進入recursive()後準備執行businessLogic(),等等問題來了!之前執行的businessLogic()的鎖還沒有放開這次又執行到這裏了,當然是過不去的了,形成了死鎖!從這個例子我們總結出來一個規律就是在遞歸的時候在遞歸鏈上面的方法上加鎖肯定會出現死鎖(所謂遞歸鏈就是指recursive()鏈向businessLogic(),businessLogic()又鏈回recursive()),解決這個問題的方法就是避免在遞歸鏈上加鎖,請看以下的例子

 

saveToDB()不在這條遞歸鏈上面自然不會出現死鎖,所以說在遞歸中加鎖是件很危險的事情,實在逃不過要加鎖就加在最小的粒度的程序代碼上以減小死鎖的概率。

 

 

發佈了23 篇原創文章 · 獲贊 4 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章