JAVA併發編程

併發編程詳解

注意點:

1、不要調用常量字符串或全局對象的 wait() 方法

通過常量字符串 String 來調用 wait() 或 notify() 方法所導致的問題是,JVM/編譯器會在內部自動將內容相同的 String 轉變爲相同的對象。這意味着,即便你創建了兩個不同的 MyWaitNotify 實例,他們內部的 myMonitorObject 變量也會指向相同的 String 對象。這將導致一個非預期的線程中的 notify() 方法會喚醒另睡眠中的線程。
2、volatile 關鍵字保證了一個變量在不同線程間的可見性。
3、volatile前提: 多個線程在讀寫 volatile 變量時新計算並寫入的變量值不依賴於其歷史值,換言之,就是說只要一個線程在寫入一個 volatile 變量之前不需要先讀取它再計算出新的數值,就不會出現問題。單個寫,不受此限制。
4、由於 volatile 變量的讀寫都要直達主內存,而讀寫主內存要比讀寫 CPU 的緩存更加的費時
5、如果一段代碼沒持有一個對象的鎖,就無法調用這個對象的 wait(),notif() 或 notifyAll() 方法。
6、在一些情況下死鎖的情況是可以被預防的,三種相關的技術 —— 順序鎖(Lock Ordering)、超時鎖(Lock Timeout)和死鎖探測(Deadlock Detection)
順序鎖:死鎖發生在多個線程都需要相同的(一系列)鎖,但是所需要的順序不同。如果可以確定任意的線程都以相同的順序使用這些鎖,那麼死鎖就不會發生。這種不同的線程間通以相同的順序使用鎖來預防死鎖發生的辦法就稱爲順序鎖。
順序鎖是一個簡單但高效的預防死鎖的機制。但其前提是要預先知道所有鎖的執行順序,然而這有時是無法辦到的。
超時鎖:一個線程只會嘗試獲取一個鎖一段時間,隨後就放棄。如果時限內這個線程沒能成功的獲取其本想獲得的全部的鎖,這個線程就會備份並釋放掉已經獲得的全部鎖資源,再等待一個隨機的時長,然後重新從頭開始嘗試。
然而,即便觸發了超時,也不一定意味着發生了死鎖,很可能只是一個線程所處理的任務非常費時。另外一點是,即便引入超時的機制,如果足夠多的線程競爭相同的鎖,依舊會使得死鎖的發生概率很高。
死鎖探測:每次一個線程實際獲得一個鎖時,那麼就在某個數據結構(映射、圖等,你可以自己定義)中做一個獲得標記;每當一個線程需要獲得一個鎖時,也將在這個數據結構中做一個需求標記。如此一來,當一個線程請求一個鎖但是無法獲得時,這個線程就遍歷這個數據結構來探測死鎖。
探測到死鎖後,一種可能的解決方案是所有的線程釋放掉所有的鎖,每個等待一個隨機的時長然後重新嘗試,但問題同上。一個更好的選擇是爲不同的線程分配優先級,使得只有一個或很少的線程釋放掉其持有的鎖資源,而其他的線程正常運作。但是如果各個線程間的優先級是確定的話,相同的線程可能總是獲得較低的優先級而無法被執行。爲了避免這一情況發生,需要在死鎖發生時隨機分配優先級。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章