Java高併發13-公平鎖與非公平鎖、自旋鎖、可重入鎖

1、複習

  • 僞共享原理以及如何避免
  • 樂觀鎖和悲觀鎖

2、公平鎖與非公平鎖

  • 按照線程請求並得到鎖的時間順序,能夠將鎖分爲公平鎖和非公平鎖
  • 公平鎖:線程獲取鎖的順序是按照線程請求鎖的時間遲早來進行劃分的,也就是知足先到先得的原則;
  • 非公平鎖:線程在運行時闖入的,並非按照先到先得的原則。

1.Java中兩種鎖的實現機制

  • Reentrant reentrant = new Reentrant(true)表明公平鎖
  • Reentrant reentrant = new Reentrant(false)表明非公平鎖
  • 若是構造函數不傳入參數的話,那麼默認就是非公平鎖
  • 在沒有公平性需求的前提下儘可能使用非公平鎖,由於公平鎖會帶來額外的開銷。

3、獨佔鎖和共享鎖

  • 按照一個資源是否能夠同時被多個線程持有,或者只能被一個線程持有,能夠分爲獨佔鎖和共享鎖。ReetrantLock鎖是一個獨佔鎖,同一時間只能由一個線程所持有;共享鎖能夠由多個線程共同 持有,好比:ReadWriteLock.
  • 獨佔鎖是一中悲觀鎖,這種必須先加排他鎖才能對資源進行訪問,限制了併發性;共享鎖是一中樂觀鎖,這種放寬了加鎖的條件,容許多個線程可以同時訪問資源。

4、可重入鎖

  • 定義:當一個線程要獲取一個被其餘線程佔有的獨佔鎖時,該線程會被阻塞,那麼當一個線程獲取它本身已經獲取的鎖時是否會被阻塞起來呢?若是不會阻塞就能夠稱爲 可重入鎖
  • 下面舉一個例子
package com.ruigege.PricipleAnalyzingOfThreadLocalRandom3;

public class Hello {
 public static void main(String[] args) {
  new Hello().helloB();
 }
 
 public synchronized void helloA() {
  System.out.println("HelloA");
 }
 
 public synchronized void helloB() {
  System.out.println("HelloB");
  helloA();
 }
}
13.1
13.1
  • 代碼解析:hellB方法調用,會先獲取得內置鎖,而後打印輸出,以後調用helloA方法,在調用以前會先獲取內置鎖,若是內置鎖不是可重入的,那麼調用線程將會一直阻塞。
  • 實際上synchronized內部鎖是一個可重入鎖,可重入鎖的原理是在鎖的內部維護一個線程標示,用於標示該鎖目前正在被哪一個線程佔用,而後關聯一個計數器,一開始計數器值爲0,說明該鎖沒有被任何線程佔用,當一個線程獲取了該鎖時,計數器的值會變成1,計數器的值會變成1,這時其餘線程再來獲取該鎖時會發現鎖的全部者不是本身而被阻塞掛起。可是當獲取了該鎖的線程再次得到鎖的時候發現鎖的擁有者時本身,就會把計算器值+1,當釋放鎖後計數器-1,當計數器爲0的時候,鎖裏面的線程標示被重置爲null,這時候被阻塞的線程會被喚醒來競爭獲取該鎖。

5、自旋鎖

  • 因爲Java中的線程是與操做系統中的線程一一對應的,因此當一個線程在獲取鎖(好比獨佔鎖)失敗後,會被切換到內核狀態而被掛起,當該線程獲取到鎖時又須要將其切換到內核狀態而喚醒該線程。而從用戶狀態切換到內核狀態的開銷是比較大的,在必定程度上會影響併發性能,自旋鎖則是,當前線程在獲取鎖的時候,若是發現這個鎖已經被其餘的鎖佔用了,他不能立刻阻塞本身,在不放棄CPU使用權的狀況下,屢次嘗試獲取(默認次數10,可使用-XX:PreBlockSpinsh參數設置該值),頗有可能在後面幾回嘗試中其餘線程已經釋放了鎖,若是嘗試指定的次數後仍沒有獲取到鎖則當前線程纔會被阻塞掛起,由此看來自旋鎖時使用了CPU時間獲取線程阻塞與調度的開銷,可是頗有可能這些CPU時間白白浪費了。

6、源碼:

相關文章
相關標籤/搜索