2017-10-09 52 views
4

我不知道是否像可能性例外的嘗試,終於

lock.lock(); 
try { 
    count++; 
} finally { 
    lock.unlock(); 
} 

給出的代碼有任何機會,執行的線程可以執行lock方法之後,但在進入try-finally塊之前以某種方式結束?這會導致一個鎖,但從未釋放。 Java/JVM規範中是否有一些行可以保證,如果代碼是使用該慣用語編寫的,那麼就沒有機會離開永遠鎖定的鎖?

我的問題是,答案爲C#相關問題Can Monitor.Enter throw an exception?上MSDN

  1. https://blogs.msdn.microsoft.com/ericlippert/2007/08/17/subtleties-of-c-il-codegen/
  2. https://blogs.msdn.microsoft.com/ericlippert/2009/03/06/locks-and-exceptions-do-not-mix/

有關問題,這樣的代碼

Monitor.Enter(...) 
try 
{ 
    ... 
} 
finally 
{ 
    Monitor.Exit(..) 
} 
引用兩個帖子的啓發

th在C#的情況下,基於由JIT生成的機器代碼,執行永遠不會達到try-finally的可能性很小。

我知道這可能被看作是一個人爲的問題/挑剔,但我的好奇心好轉了我。

+2

也許與爲什麼[Thread.stop()已棄用](https://stackoverflow.com/questions/16504140/thread-stop-deprecated)相關。從來沒有聽說過類似Java中鏈接的noop這樣的問題,但當然這也會依賴於JVM。 – Kayaman

+0

相關:https://stackoverflow.com/questions/31058681/java-locking-structure-best-pattern – assylias

+0

出於所有實際的目的,您可以假設拋出異常並且鎖未鎖定,否則您將輸入try塊。顯然,這不包括突然的JVM崩潰等,在這種情況下鎖發生了什麼並不重要... – assylias

回答

3

有沒有用Java/JVM規範一些行,讓我們的任何 保證,如果代碼是使用成語寫沒有 機會離開鎖定永遠鎖?

首先我想注意的是,在java中有結構化和非結構化的鎖。結構化鎖是那種鎖,其中鎖定的代碼被封裝在某個結構(塊)內並且在該結構(塊)的末尾被自動釋放。有與synchronized塊同步的方法。在Java中,只有在使用同步塊的情況下,纔會直接在字節碼中獲取monitorenter(https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.monitorenter)指令。所以如果monitorenter指令失敗,那麼這將是一個致命的錯誤,這將導致jvm停止。在同步方法中,編譯字節碼中沒有monitorenter和monitorexit指令,但同步塊中的代碼標記爲jvm同步,jvm將自行完成該任務。所以在這種情況下,如果smth會出錯,那麼這將是一個致命的jvm錯誤。所以在這裏你的問題的答案是否定的,因爲同步塊或方法被編譯爲本地jvm指令,它們的崩潰將導致整個jvm崩潰。

現在讓我們來談談非結構化鎖。這些是鎖,您必須通過調用該鎖的直接方法來關注鎖定和解鎖。在這裏,您可以獲得創建複雜有趣的結構(如鎖鏈等)的諸多優點。而且,對於你的問題的答案是否定的,實際上,在這種方法中拋出異常是完全可能的,並且在這裏也可能得到活鎖或死鎖。所有這些都是可能的,因爲非結構化鎖定是絕對的程序化的Java概念。 JVM對非結構化鎖定一無所知。鎖定在java中是一個接口(https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Lock.html),您可以使用OOB非結構化鎖定,如Reentrant鎖定,Reentrant RW鎖定等,或編寫您的自定義實現。但在現實生活中,如果您正在使用例如可重入鎖,那麼幾乎沒有機會在那裏獲得例外。即使是靜態分析器也會說你在RLock中沒有任何可以拋出異常的地方(檢查爲未選中)。但是有可能的是在那裏出現錯誤(https://docs.oracle.com/javase/7/docs/api/java/lang/Error.html)。我們再次遇到致命的JVM故障,之後您不需要任何鎖。而不是字節碼的監視器RLock,而幾乎所有其他的OOB java鎖都使用AbstractQueuedSynchronizer(https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/AbstractQueuedSynchronizer.html)。所以你可以確定它是完全程序化的,而且JVM對此幾乎一無所知。

現在從架構的角度來看。如果在某些實現中,你在鎖方法中有一個意外的異常,並且在該鎖仍然可用於進一步使用的情況下,那麼最好是在那裏獲得永久生存的鎖,而不是具有破壞的內部狀態的鎖。它不再安全地使用它,沒有人保證正確的進一步鎖定,因爲你至少有一個不正確行爲的先例。在進一步使用前,任何意外的鎖定異常都應視爲需要深入調查的初始原因。長時間的鎖定會阻止其他線程的使用,更重要的是,系統會保持它的正確狀態。那麼當然有一天smb將m併發計算通常主要是關於正確性。

現在關於這個問題:

是有任何機會,執行的線程可以在某種程度上執行鎖方法之後,但在進入的try-finally塊前終止 ?

答案是肯定的。你甚至可以暫停線程持有鎖或只是調用睡眠,以便其他線程不會獲得它。這就是鎖定算法的工作方式,我們無法做到這一點。這將被分類爲一個錯誤。實際上,無鎖2+線程算法在這種情況下不容易受到攻擊。併發編程不是一件簡單的事情。在設計過程中你應該考慮很多事情,甚至在那之後你不會避免失敗。

+0

這是一個非常徹底和務實的答案。 – theMayer