1. 程式人生 > >Volatile和synchronized區別、死鎖概念

Volatile和synchronized區別、死鎖概念

1、Volatile變數和同步機制synchronized 、Lock區別

1、volatile變數是一種稍弱的同步機制在訪問volatile變數時不會執行加鎖操作,因此也就不會使執行執行緒阻塞,因此volatile變數是一種比synchronized關鍵字更輕量級的同步機制。

2、從記憶體可見性的角度看,寫入volatile變數相當於退出同步程式碼塊,而讀取volatile變數相當於進入同步程式碼塊。

3、在程式碼中如果過度依賴volatile變數來控制狀態的可見性,通常會比使用鎖的程式碼更脆弱,也更難以理解。僅當volatile變數能簡化程式碼的實現以及對同步策略的驗證時,才應該使用它。一般來說,用同步機制會更安全些。

4、加鎖機制(即同步機制)既可以確保可見性又可以確保原子性,而volatile變數只能確保可見性,原因是宣告為volatile的簡單變數如果當前值與該變數以前的值相關,那麼volatile關鍵字不起作用,也就是說如下的表示式都不是原子操作:“count++”、“count = count+1”。

當且僅當滿足以下所有條件時,才應該使用volatile變數:

1、對變數的寫入操作不依賴變數的當前值,或者你能確保只有單個執行緒更新變數的值。

2、該變數沒有包含在具有其他變數的不變式中。

區別總結:在需要同步的時候,第一選擇應該是synchronized關鍵字,這是最安全的方式,嘗試其他任何方式都是有風險的。尤其在、jdK1.5之後,對synchronized同步機制做了很多優化,如:自適應的自旋鎖、鎖粗化、鎖消除、輕量級鎖等,使得它的效能明顯有了很大的提升。

2、多執行緒與死鎖

死鎖現象:當兩個或兩個以上程序在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們將一直阻塞下去。

例如:執行緒A當前持有互斥所鎖lock1,執行緒B當前持有互斥鎖lock2。接下來,當執行緒A仍然持有lock1時,它試圖獲取lock2,因為執行緒B正持有lock2,因此執行緒A會阻塞等待執行緒B對lock2的釋放。如果此時執行緒B在持有lock2的時候,也在試圖獲取lock1,因為執行緒A正持有lock1,因此執行緒B會阻塞等待A對lock1的釋放。二者都在等待對方所持有鎖的釋放,而二者卻又都沒釋放自己所持有的鎖,這時二者便會一直阻塞下去。這種情形稱為死鎖。

產生死鎖的四個必要條件:

(1) 互斥條件:一個資源每次只能被一個程序使用。

(2) 請求與保持條件:一個程序因請求資源而阻塞時,對已獲得的資源保持不放。

(3) 不剝奪條件:程序已獲得的資源,在未使用完之前,不能強行剝奪。

(4) 迴圈等待條件:若干程序之間形成一種頭尾相接的迴圈等待資源關係。

舉例:

T1、T2表示兩個任務;R1和R2表示兩個資源;由資源指向任務的箭頭(如R1->T1,R2->T2)表示該資源被改任務所持有;由任務指向資源的箭頭(如T1->S2,T2->S1)表示該任務正在請求對應目標資源;

    其滿足上面死鎖的四個必要條件:

(1).互斥:資源S1和S2不能被共享,同一時間只能由一個任務使用;

(2).請求與保持條件:T1持有S1的同時,請求S2;T2持有S2的同時請求S1;

(3).非剝奪條件:T1無法從T2上剝奪S2,T2也無法從T1上剝奪S1;

(4).迴圈等待條件:上圖中的箭頭構成環路,存在迴圈等待。

避免死鎖的辦法:

(1)阻止迴圈等待條件,將系統中所有的資源設定標誌位、排序,規定所有的程序申請資源必須以一定的順序(升序或降序)做操作來避免死鎖。

(2)只在必要的最短時間內持有鎖,考慮使用同步語句塊代替整個同步方法;

(3)儘量編寫不在同一時刻需要持有多個鎖的程式碼,如果不可避免,則確保執行緒持有第二個鎖的時間儘量短

(4)建立和使用一個大鎖來代替若干小鎖,並把這個鎖用於互斥,而不是用作單個物件的物件級別鎖。