1. 程式人生 > >java可重入鎖ReentrantLock原理

java可重入鎖ReentrantLock原理

ReentrantLock

JUC中ReentrantLock實現了獨佔模式重入鎖,對於可重入,此類的註釋是這樣的:

當一個執行緒鎖住ReentrantLock,但是沒有解鎖,這個執行緒再執行加鎖方法會返回成功,並獲得這把鎖

ReentrantLock類圖

這裡寫圖片描述
由上圖可知,ReentrantLock包含兩個靜態屬性,一個是java 序列化Serializable使用的屬性serialVersionUID,一個是sync,sync是內部類Sync的例項,ReentrantLock中還包含兩個另外兩個子類FairSync,NonfairSync。它們都繼承自Sync類。FairSync是公平鎖的實現方式,NonfairSync是非公平鎖的實現方式。他們兩都實現了可重入。

ReentrantLock初始化

ReentrantLock有兩個不同的初始化方法

public ReentrantLock() {
       //預設方式:設定sync屬性為非公平模式鎖NonfairSync例項
       sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
   //根據使用者傳入是否公平引數,設定sync屬性為非公平模式鎖NonfairSync或者公平模式鎖FairSync例項
   sync = fair ? new FairSync() : new NonfairSync();
}

ReentrantLock加鎖解鎖

ReentrantLock解鎖解鎖執行的都是sync的方法

//加鎖
public void lock() {
    sync.lock();
}
//可中斷加鎖
public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}
//嘗試加鎖
public boolean tryLock() {
    return sync.nonfairTryAcquire(1);//使用非公平模式獲取一次鎖
}
//帶時間限制的嘗試加鎖
public
boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } //釋放鎖 public void unlock() { sync.release(1); }

Sync加鎖解鎖

Sync的加鎖方法是一個abstract 方法,交由子類實現

abstract void lock();

公平重入鎖

其中公平重入鎖方式直接呼叫aqs的acquire,然後自身實現了aqs acquire方法會呼叫的tryAcquire方法(aqs的流程詳見上一篇aqs的介紹)。其中tryAcquire獲取鎖首先會判斷當前state是否為0。
如果不為0則代表鎖已被鎖住,如果被鎖住,則檢視佔用的執行緒是否為當前執行緒。如果是當前執行緒佔用了這個鎖,則可重入,進行state增加。state可以表示持有者重入次數。
如果為0,則判斷當前執行緒代表的節點在佇列中有沒有在排隊的前任,如果沒有在排隊的前任則嘗試獲取鎖。因為新加入獲取鎖佇列的節點都是加在佇列的尾部。而只有佇列中沒有前任在排隊獲取鎖的節點才能嘗試獲取鎖,維持了一個先進先出的規則。所有執行緒都是根據進入順序去持有鎖的,是公平的方式。

protected final boolean tryAcquire(int acquires) {
     final Thread current = Thread.currentThread();
     int c = getState();
     if (c == 0) {
         //佇列中沒有前任在排隊獲取鎖,則嘗試獲取鎖
         if (!hasQueuedPredecessors() &&
             compareAndSetState(0, acquires)) {
             //設定擁有者為自己
             setExclusiveOwnerThread(current);
             return true;
         }
     }
     //如果持有鎖的為自己,則重入state+=acquires
     else if (current == getExclusiveOwnerThread()) {
         int nextc = c + acquires;
         if (nextc < 0)
             throw new Error("Maximum lock count exceeded");
         setState(nextc);
         return true;
     }
     return false;
 }

非公平重入鎖

非公平重入鎖NonfairSync 在呼叫lock方法時會快速cas嘗試修改state狀態0->1,如果成功則設定鎖持有者為自身,不去管是否有其他執行緒在等待。是非公平的方式。當cas不成功時,則呼叫acquire,並且NonfairSync 自身也和FairSync類一樣,實現了acquire會呼叫的tryAcquire方法。tryAcquire方法使用的是父類Sync的非公平嘗試獲取鎖方法nonfairTryAcquire

protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

nonfairTryAcquire方法與FairSync類的tryAcquire不同點在於這個方法實現的不公平方式不會判斷是否有前任在排隊。直接進行嘗試獲取鎖。會造成插隊情況,是不公平獲取鎖的形式,但是比公平方式少了一些判斷,效能更優。
nonfairTryAcquire的重入方式與公平鎖FairSync的一樣。

final boolean nonfairTryAcquire(int acquires) {
  final Thread current = Thread.currentThread();
  int c = getState();
  if (c == 0) {
      //如果狀態為0,當前鎖未鎖住,嘗試cas修改狀態
      if (compareAndSetState(0, acquires)) {
          //設定當前執行緒為鎖的獨佔執行緒
          setExclusiveOwnerThread(current);
          return true;
      }
  }
  //如果佔據鎖的執行緒是當前執行緒,state+=acquires
  else if (current == getExclusiveOwnerThread()) {
      int nextc = c + acquires;
      if (nextc < 0) // overflow
          throw new Error("Maximum lock count exceeded");
      setState(nextc);
      return true;
  }

ReentrantLock unlock

ReentrantLock unlock呼叫的Sync的父類AQS的release方法釋放鎖。而Sync實現了release方法會呼叫的tryRelease。tryRelease會使鎖狀態state減少,呼叫一次則state減少1,代表重入次數減少一次。當減少到為0時,表示當前執行緒沒有持有該鎖,將鎖的持有者置為空。

 protected final boolean tryRelease(int releases) {
            //state-=releases
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                //如果state為0,設定鎖的所有者為空
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

總結

ReentrantLock的重入為當持有鎖的為當前執行緒時,重入,將鎖狀態state增加。
ReentrantLock的公平非公平模式,交由Sync的兩個子類控制,區別在於公平模式只有沒有前任在排隊的情況下才可以獲取鎖。公平模式不進行這個判斷。直接嘗試獲取鎖,可插隊。

相關推薦

javaReentrantLock原理

ReentrantLock JUC中ReentrantLock實現了獨佔模式重入鎖,對於可重入,此類的註釋是這樣的: 當一個執行緒鎖住ReentrantLock,但是沒有解鎖,這個執行緒再執行加鎖方法會返回成功,並獲得這把鎖 Reentrant

java(ReentrantLock)的實現原理

聲明 其他人 不用 vol 喚醒 ola 是不是 結束 真的 前言相信學過java的人都知道 synchronized 這個關鍵詞,也知道它用於控制多線程對並發資源的安全訪問,興許,你還用過Lock相關的功能,但你可能從來沒有想過java中的鎖底層的機制是怎麽實現的。如果真

輕鬆學習java(ReentrantLock)的實現原理

前言 相信學過java的人都知道 synchronized 這個關鍵詞,也知道它用於控制多執行緒對併發資源的安全訪問,興許,你還用過Lock相關的功能,但你可能從來沒有想過java中的鎖底層的機制是怎麼實現的。如果真是這樣,而且你有興趣瞭解,今天我將帶領你輕鬆

漫畫圖解java(ReentrantLock)的實現原理

前言 相信學過java的人都知道 synchronized 這個關鍵詞,也知道它用於控制多執行緒對併發資源的安全訪問,興許,你還用過Lock相關的功能,但你可能從來沒有想過java中的鎖底層的機制是怎麼實現的。如果真是這樣,而且你有興趣瞭解,今天我將帶領你輕鬆的學習下j

JavaReentrantLock

在JDK1.5之前,我們設計程式進行執行緒之間通訊時必須使用同步鎖,獲得同步鎖必須使用同步關鍵字synchronized(lock)的形式。JDK1.5提供了執行緒同步相關的包java.util.concurrent,引入了可重入鎖ReentrantLock,使用起來很方便並且提高了程式碼執

Java原始碼解析之ReentrantLock(二)

上文接Java原始碼解析之可重入鎖ReentrantLock(一)。 接下來是tryLock方法。程式碼如下。從註釋中我們可以理解到,只有當呼叫tryLock時鎖沒有被別的執行緒佔用,tryLock才會獲取鎖。如果鎖沒有被另一個執行緒佔用,那麼就獲取鎖,並立刻返回true,並把鎖計數設定為1.

Java原始碼解析之ReentrantLock(一)

本文基於jdk1.8進行分析。 ReentrantLock是一個可重入鎖,在ConcurrentHashMap中使用了ReentrantLock。 首先看一下原始碼中對ReentrantLock的介紹。如下圖。ReentrantLock是一個可重入的排他鎖,它和synchronized的方法

Java與其釋放

在學習Java多執行緒相關的知識時,通常都會涉及鎖這個概念,常見的synchronized、Lock均為可重入鎖。為了更好的理解可重入鎖,需要先理解一下幾個問題: 1、誰持有了鎖?   2、鎖的物件是誰? 3、可重入鎖的可重入是什麼意思? 一、synchronized關鍵字

java併發-(ReentrantLock)

文章目錄 重入鎖(ReentrantLock) 含義 公平鎖和非公平鎖 公平與非公平效能 使用 [GitHub主頁](https://github.com/yinzhongzheng/study)

Java 實現原理

ReentrantLock 是java繼synchronized關鍵字之後新出的執行緒鎖,今天看了看實現原始碼。主要是通過自旋來實現的。使用自旋的基本思路就是為所有的執行緒構建一個node,連成一個佇列,然後每一個node都輪詢前驅節點,如果前驅已經釋放鎖了,那麼當前階段就

Java併發基礎-的使用及原理、讀寫、內建、訊號量等)

本文目錄: 1 基礎 1.1 可重入鎖 可重入鎖表示的是,如果一個執行緒在未釋放已獲得鎖的情況下再次對該物件加鎖,將可以加鎖成功。而且可以不斷的加鎖成功多次。但需要注意的是,每次加鎖操作必須對應著一次釋放鎖的操作。 如以下示例是可以執行的(

ReenTrantLock(和synchronized的區別)總結

循環 比較 關於 兩種方法 sdn 一個 隨機 targe condition ReenTrantLock可重入鎖(和synchronized的區別)總結 可重入性: 從名字上理解,ReenTrantLock的字面意思就是再進入的鎖,其實synchronized關鍵字所使用

Java併發程式設計:自己動手寫一把

關於執行緒安全的例子,我前面的文章Java併發程式設計:執行緒安全和ThreadLocal裡面提到了,簡而言之就是多個執行緒在同時訪問和修改公共資源的時候,由於不同執行緒搶佔CPU問題而導致的結果不確定性,就是在併發程式設計中經常要考慮的執行緒安全問題。前面的做法是使用同步語句synch

Java並發(九): ReentrantLock

阻塞隊列 輪詢 cee condition 源碼分析 interrupt 同步 iter 投票 一、ReentrantLock類結構 public class ReentrantLock implements Lock, java.io.Serializable {

ReentrantLock(不看後悔,看了必懂)

ReentraantLock是通過一個FIFO的等待佇列來管理獲取該鎖所有執行緒的。在“公平鎖”的機制下,執行緒依次排隊獲取鎖(先等待的執行緒先獲得鎖);而“非公平鎖”在鎖是可獲取狀態時,不管自己是不是在佇列的開頭都會獲取鎖。  ReentrantLock和synchroni

Java併發程式設計:用AQS寫一把

前一篇部落格Java併發程式設計:自己動手寫一把可重入鎖詳述瞭如何用synchronized同步的方式來實現一把可重入鎖,今天我們來效仿ReentrantLock類用AQS來改寫一下這把鎖。要想使用AQS為我們服務,首先得弄懂三個問題:AQS是什麼?AQS已經做了什麼以及我們還需要做些什

Java併發程式設計:

關於執行緒安全的例子,簡而言之就是多個執行緒在同時訪問或修改公共資源的時候,由於不同執行緒搶佔公共資源而導致的結果不確定性,就是在併發程式設計中經常要考慮的執行緒安全問題。現在嘗試來用Lock顯式加鎖來解決執行緒安全的問題,先來看一下Lock介面的定義: public interface L

Java同步處理底層實現(monitor )

對於Java同步處理可以參考這篇部落格:https://blog.csdn.net/sophia__yu/article/details/83990755 但是這些處理方法的底層實現是怎樣呢? 接下里將會解釋同步程式碼塊、同步方法、全域性鎖的底層實現。 同步程式碼塊底層實現: 首

【JVM第八篇】:Java併發程式設計:用AQS寫一把

前一篇部落格Java併發程式設計:自己動手寫一把可重入鎖詳述瞭如何用synchronized同步的方式來實現一把可重入鎖,今天我們來效仿ReentrantLock類用AQS來改寫一下這把鎖。要想使用AQS為我們服務,首先得弄懂三個問題:AQS是什麼?AQS已經做了什麼以及我們還需要做些什麼?

【JVM第七篇】:Java併發程式設計:自己動手寫一把

關於執行緒安全的例子,簡而言之就是多個執行緒在同時訪問或修改公共資源的時候,由於不同執行緒搶佔公共資源而導致的結果不確定性,就是在併發程式設計中經常要考慮的執行緒安全問題。前面的做法是使用同步語句synchronized來隱式加鎖,現在我們嘗試來用Lock顯式加鎖來解決執行緒安全的問題,先來