天天看點

Java并發(五)——鎖鎖

文章目錄

    • 1 死鎖
    • 2 鎖的類型
      • 2.1 樂觀鎖和悲觀鎖
      • 2.2 公平鎖和非公平鎖
      • 2.3 獨占鎖和共享鎖
      • 2.4 讀寫鎖和排他鎖
      • 2.5 可重入鎖
      • 2.6 自旋鎖
    • 3 鎖的等級
      • 3.1 偏向鎖
      • 3.2 輕量級鎖
      • 3.3 重量級鎖
      • 3.4 總結

1 死鎖

死鎖是指兩個或兩個以上的線程在執行任務過程中,因争奪資源而造成的互相等待的現象

産生死鎖的條件:

  • 互斥條件:指線程對已經擷取的資源進行排他性使用,即該資源同時隻能由一個線程占用
  • 請求并持有條件:指線程已經持有了至少一個資源,但又請求其他資源,如果請求不到則阻塞,但不釋放已有的資源
  • 不可剝奪條件:指線程擷取到資源後,在使用完之前不能被其他線程占用
  • 環路等待條件:指發生死鎖時,必然存在一個線程——資源的環形鍊

避免死鎖:破壞死鎖産生的條件

  • 破壞請求并持有條件:一次性擷取所有所需的資源
  • 破壞不可剝奪條件:占用資源的線程再擷取其他資源擷取不到時,釋放已有的資源
  • 破壞環路等待條件:按某一順序申請資源,釋放資源則按相反的順序

2 鎖的類型

2.1 樂觀鎖和悲觀鎖

悲觀鎖:指對資料被外界的修改持悲觀态度,認為資料很容易被修改。是以操作前會先進行加鎖,操作結束再釋放鎖。悲觀鎖的實作往往依靠資料庫提供的鎖機制

樂觀鎖:與悲觀鎖相反。是以在通路資料時不會加鎖,隻有對資料送出更新的時候,才會檢測資料是否沖突。樂觀鎖通常通過version字段(CAS)或者使用業務狀态來實作

2.2 公平鎖和非公平鎖

公平鎖:線程擷取鎖的順序是按照線程請求鎖的順序

非公平鎖:通過搶占的方式來争奪鎖

2.3 獨占鎖和共享鎖

獨占鎖:一種悲觀鎖,保證任何時候僅有一個線程可以獲得鎖

共享鎖:一種樂觀鎖,允許多個線程同時進行讀操作

2.4 讀寫鎖和排他鎖

排他鎖:同一時刻隻允許一個線程通路

讀寫鎖:内部維護一個讀鎖一個寫鎖,通過分離讀鎖和寫鎖,在讀多寫少情況下提高性能

2.5 可重入鎖

當線程再次擷取一個自己已經擷取的鎖時,如果不會被阻塞,則該鎖是可重入鎖

原理:在鎖内部維護一個辨別,辨別哪個線程占用,一個計數器,計數該線程擷取鎖的次數

2.6 自旋鎖

當線程在擷取鎖時,如果發現鎖已經被占用,不會馬上阻塞自己,而是不放棄CPU使用權的情況下,多次嘗試擷取

3 鎖的等級

3.1 偏向鎖

偏向鎖在資源無競争情況下消除了同步語句,連CAS操作都不做了,提高程式的運作性能

一個線程在進入同步塊時,會檢查鎖的MarkWord裡是不是自己的線程ID

如果是,則表明該線程已經擷取偏向鎖,以後該線程進入和退出同步塊不需要花費CAS操作加鎖和解鎖

如果不是,則代表有另一個線程競争偏向鎖,嘗試用CAS來修改MarkWord中的線程ID。如果成功,表示另一個線程不存在了(釋放鎖了),如果失敗,則更新為輕量級鎖

Java并發(五)——鎖鎖

3.2 輕量級鎖

線程嘗試用CAS修改MarkWord中的線程ID,如果失敗,則采用自旋來擷取鎖

如果自旋到一定程度,依然沒有擷取鎖,則更新為重量級鎖

Java并發(五)——鎖鎖

3.3 重量級鎖

重量級鎖依賴于作業系統中的互斥量來實作

3.4 總結

偏向鎖

  • 優點:加鎖和解鎖不需要額外的消耗
  • 缺點:如果線程間存在鎖競争,則會帶來額外的鎖撤銷的消耗
  • 适用:隻有一個線程通路同步塊

輕量級鎖

  • 優點:競争的線程不會阻塞,提高了程式的響應速度
  • 缺點:自旋會消耗CPU
  • 适用:追求相應時間,同步塊執行速度非常快

重量級鎖

  • 優點:不使用自旋,不會消耗CPU
  • 缺點:線程阻塞,響應時間緩慢
  • 适用:追求吞吐量,同步塊執行時間較長