1. 程式人生 > 其它 >併發程式設計3 鎖

併發程式設計3 鎖

java鎖

  1.樂觀鎖

  樂觀鎖是一種思想,認為讀多寫少,遇到併發修改的可能性低。每次修改資料之前都認為別人不會修改所以不用上鎖,但是在修改值後會比較修改前和修改後的版本號。如果版本號一致則認為更新有效,如果版本號發生了改變則認為更新無效。java中的樂觀鎖基本都是通過CAS操作實現的。CAS是一種原子操作。

  2.悲觀鎖

  悲觀鎖恰恰與樂觀鎖相反,認為讀少寫多,遇到併發修改的可能性高。每次讀寫資料前線對資料上鎖,這樣別人就不能對資料進行操作指導自己完成修改後釋放鎖。java中的悲觀鎖通過Synchronized實現。AQS框架下會先嚐試樂觀鎖更新,樂觀鎖失敗後會轉為悲觀鎖。

  3.自旋鎖

  4.synchronized同步鎖

  synchronized的作用範圍:

    1.作用於非靜態方法時,鎖住的是物件的例項(this).

    2.作用於靜態方法時,鎖住的是Class例項。因為Class相關資料儲存在永久代/元空間(metaspace),是全域性共享的。所以此時是一個全域性鎖。

    3.synchronized鎖住物件例項時,鎖住了所有的以該物件為鎖的程式碼塊。同理sunchronized鎖住的是Class例項時鎖住的也是所有以Class例項為鎖的程式碼塊。

  synchronized核心元件:

    1. Wait Set: 那些呼叫wait方法的被阻塞的執行緒放在這裡。

    2.Contention List:競爭佇列,所有請求所的執行緒首先被放在這個競爭佇列中。  

    3.Entry List:Contention List中有資格成為候選資源的執行緒被移動到Entry List中。

    4.OnDeck:任意時刻,最多隻有一個執行緒正在競爭鎖資源,該執行緒被稱為OnDeck。

    5.Owner:當前已經獲取到鎖資源的執行緒稱為Owner。

    6.!Owner:當前釋放鎖的執行緒。

  synchronized實現:

    

    1.JVM每次從佇列的尾部取出一個數據用於競爭鎖的候選者(OnDeck),但是在併發情況下,ContentionList會被大量的併發執行緒進行CAS訪問。為了降低對尾部元素的競爭,JVM會將一部分執行緒移動到EntryList中作為候選競爭執行緒。

    2.Owner執行緒在unlock時,將ContentionList中的部分執行緒遷移到EntryList中,並指定EntryList中的某個執行緒為OnDeck執行緒(一般就是第一個進入進入集合的執行緒)。

    3.Owner執行緒並不直接把鎖傳遞給OnDeck執行緒,而是吧鎖競爭的權利交給OnDeck,OnDeck需要重新競爭鎖。這樣雖然犧牲了一些公平性,但是可以極大的提升系統的吞吐量,在JVM中,也把這種選擇行為稱為“競爭切換”。

    4.OnDeck執行緒在獲取到鎖後稱為新的Owner,而沒有獲取到資源的執行緒任然留在EntryList中。如果Owner被wait阻塞則轉移到WaitSet佇列中,直到某個時刻被notify或者notifyAll喚醒會重新進入到EntryList中等待稱為OnDeck。

    5.處理ContentionList、EntryList、WaitSet中的執行緒都處於阻塞狀態,該阻塞是由作業系統來完成的。

    6.synchronized是非公平鎖。synchronized線上程進入ContentionList時,等待的執行緒會先嚐試自選獲取鎖,如果自選沒有獲取到鎖則進入ContentionList。這明顯對已經進入佇列的執行緒是不公平的,還有一個不公平的事情就是自選獲取鎖的時候還有可能直接搶佔OnDeck執行緒的鎖資源。

    7.每個物件都有個monitor物件,加鎖就是在競爭monitor物件。程式碼塊加鎖是在前後分別加上monitorenter和monitorexit指令來實現的,方法加鎖是通過一個標記為來判斷的。

    8.synchronized是一個重量級操作,需要呼叫作業系統相關介面,效能是低效的,有可能給執行緒加鎖消耗的時間比有效的操作的時間更長。

    9.java1.6對synchronized進行了很多的優化,有適應自旋、鎖消除、鎖粗化、輕量級鎖向偏向鎖等,效率有了本質上的提高。在java1.7/1.8中都對該關鍵字的實現機制做了優化,引入了偏向鎖和輕量級鎖。都是在物件的頭中有標記位置。不需要經過作業系統加鎖。

    10鎖可從偏向鎖升級到輕量級鎖再升級到重量級鎖。這種升級的過程叫做鎖膨脹。

    11.jdk1.6中預設開啟偏向鎖和輕量級鎖,可以通過-XX:UseBiasedLocking來禁用偏向鎖。

  ReentrantLock實現:

    

public class ReentrantLockTest
{
    private static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        lock.lock();
        System.out.println("========");
        lock.unlock();
    }
}

  Condition類和Object類鎖方法的區別:

    1.Condition.await和Object物件的wait方法等效。

    2.Condition.signal方法和Object物件的notify方法等效。

    3.Condition.signalAll方法和Object物件的notifyAll方法等效。

    4.ReentrantLock類可以喚醒指定條件的執行緒,而Object物件的喚醒是隨機的。

  tryLock和lock和lockInterruptibly的區別:

    1.tryLock能獲得鎖就返回true,不能返回就立即返回false,tryLock(long timeout,TimeUnit unit)可以增加時間限制,如果超過該時間段還沒有獲得鎖,返回false。

    2.lock能獲得鎖就返回true,不能的話阻塞等待

    3.lock和lockInterruptibly,如果兩個想成分別執行這兩個方法,但此時中斷這兩個執行緒,lock不會跑出異常,而lockInterruptibly會丟擲異常。