1. 程式人生 > >Java--偏向鎖/輕量級鎖/重量級鎖

Java--偏向鎖/輕量級鎖/重量級鎖

首先要解釋清楚這幾種鎖的特徵和區別,以及執行時候的變化,需要了解Java物件頭的一些知識,在Java物件頭的Mark Word內就記錄著這些不同鎖的狀態位。另外偏向鎖---輕量級鎖---重量級鎖本文都需要依託synchronize進行理解和分析。另外也要參照網路上很多的資料。

1.物件頭:

關於物件頭的具體記錄,可以參考這邊:物件頭

2.同步的原理:

1.資料儲存的變化---(主要針對Mark Word)

1.1基礎狀態 

在執行期間,物件頭內Mark Word記憶體儲的資料會隨著鎖標誌位的變化而變化,具體分為以下4種(基於32位虛擬機器):


1.2 c++中的儲存結構分析和探究

上圖中的Mark Word是一個表格式的展示樣式,也是我從虛擬機器書中和網路上找來的,但是我還是去搜羅了一下c++中的結構資料,便於下面對於流程的分析。

1.2.1Mark Word例子解析圖


67行: 指向一個執行緒的顯示偏向鎖。

68行:一個匿名偏向鎖。

72行:輕量級鎖

73行:無鎖

74行:重量級鎖

75行:GC時使用

1.2.2 markOop中的列舉位解析:

基於上圖中的最後3位,下圖給出了列舉解釋。

我們稍加分析下幾個值:

5 == 101 就是上圖中的偏向鎖。

3 == 11 GC時使用。


2.偏向鎖

2.1偏向鎖的解釋: 

以下摘自《深入理解Java虛擬機器》,關於偏向鎖的理解,是有點複雜的,我先是把此書內的觀點摘抄下來,然後對比著理解。後續如果是藍色的字型就是摘抄的書中的原話。藉此來理解和分析。

當鎖物件第一次被執行緒獲取的時候,虛擬機器會把物件頭中的標誌位設定為01,即偏向模式。同時使用CAS操作把獲取到這個鎖的執行緒的ID記錄在物件的Mark Word中。如果操作成功,持有偏向鎖的執行緒以後每次進入這個鎖相關的同步塊時,虛擬機器都可以不再進行任何同步操作(例如,Locking,Unlocking,以及對Mark Word的update)。

當有另一個執行緒去嘗試獲取到這個鎖時,偏向模式就宣告結束。根據鎖物件目前是否處於被鎖定狀態,撤銷偏向後恢復到未鎖定狀態或升級為輕量級鎖定的狀態。

2.2偏向鎖的c++程式碼


2.2.1.此方法的入參:

1.1JavaThread(當前執行緒的值)

1.2BasicObjectLock基礎物件鎖(根據字面意思強行解釋一波)

1.2.1 BasicObjectLock結構如下:私有物件為 BasicLock 的例項 lock 和 oop 的例項 _obj。

class BasicObjectLock {
  BasicLock _lock; 
  // object holds the lock;
  oop  _obj;   
}

1.2.2 BasicLock主要儲存的是markoop物件,就是物件頭資料。

class BasicLock {
    volatile markOop _displaced_header;
}

2.2.2. 偏向鎖的流程獲取核心方法:fast_enter

在monitorenter程式碼中,可以看見如果開啟了偏向鎖的功能,那麼獲取的時候就進入了fast_enter流程。fast_enter就是這一篇主要分析的程式碼,或許理解的不夠,需要參考很多資料。此處我發現有兩個比較好的部落格,稍作記錄,兩篇部落格的觀點有點差別,具體還是見仁見智吧,因為關於fast_enter的cpp程式碼我也讀不懂~~~~~~~~~

解釋一:

1.偏向鎖的獲取:


2.偏向鎖的撤銷:


解釋二:


個人比較傾向解釋一的觀點,根據大致原始碼,發現解釋二中在判斷是否是偏向鎖的否流程中,原始碼呢看起來是傾向獲取輕量級鎖,但是此圖中確是去做CAS操作,這邊有點疑惑。。。

3.輕量級鎖的獲取和釋放

3.1原始碼簡單分析

void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
	//獲取mark word
  markOop mark = obj->mark();
  assert(!mark->has_bias_pattern(), "should not see bias pattern here");
  	
  if (mark->is_neutral()) {
	  //判斷是否是不可偏向狀態並且無鎖 0 01
    // Anticipate successful CAS -- the ST of the displaced mark must
    // be visible <= the ST performed by the CAS.
    lock->set_displaced_header(mark);  //將mark word寫入執行緒棧中的hdr中
	
    if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
	  //CAS 設定mark word指向hdr,保證只有一個執行緒能夠設定成功
      TEVENT (slow_enter: release stacklock) ;
      return ;
    }
    // Fall through to inflate() ...
  } else
  if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
	  //當前mark word指向本地執行緒棧,並且和當前執行緒一致,保證重入的特性
    assert(lock != mark->locker(), "must not re-lock the same lock");
    assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock");
    lock->set_displaced_header(NULL);
    return;
  }

#if 0
  // The following optimization isn't particularly useful.
  if (mark->has_monitor() && mark->monitor()->is_entered(THREAD)) {
    lock->set_displaced_header (NULL) ;
    return ;
  }
#endif

  // The object header will never be displaced to this lock,
  // so it does not matter what the value is, except that it
  // must be non-zero to avoid looking like a re-entrant lock,
  // and must not look locked either.
  lock->set_displaced_header(markOopDesc::unused_mark());
  //膨脹成重量級鎖
  ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}

3.2必備理論知識

3.2.1輕量級鎖的獲取當中有個很重要的概念,即執行緒棧幀中的鎖記錄(Lock Record),下圖取自周志明-虛擬機器一書


3.2.2 Stack中hdr和Lock Record存在的幾個關鍵操作步驟如下圖:


操作1:虛擬機器會線上程棧幀中建立一個Lock Record,用於儲存鎖物件目前的Mark Word。

操作2:虛擬機器使用CAS操作嘗試將物件的Mark Word更新為指向Lock Record的指標。

操作3:如果CAS成功了,將owner執向Mark Word,代表了這個執行緒擁有了該物件的鎖。

3.2.3 基本流程分析


4.重量級鎖的膨脹

ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {
  // Inflate mutates the heap ...
  // Relaxing assertion for bug 6320749.
  assert (Universe::verify_in_progress() ||
          !SafepointSynchronize::is_at_safepoint(), "invariant") ;

  for (;;) {   //自旋操作
      const markOop mark = object->mark() ;
      assert (!mark->has_bias_pattern(), "invariant") ;

      // The mark can be in one of the following states:
      // *  Inflated     - just return (膨脹完成)
      // *  Stack-locked - coerce it to inflated (輕量級鎖)
      // *  INFLATING    - busy wait for conversion to complete (膨脹中)
      // *  Neutral      - aggressively inflate the object. (無鎖)
      // *  BIASED       - Illegal.  We should never see this(偏向鎖)

      // CASE: inflated
      if (mark->has_monitor()) {
		  //如果是重量級鎖,直接返回
          ObjectMonitor * inf = mark->monitor() ;
          assert (inf->header()->is_neutral(), "invariant");
          assert (inf->object() == object, "invariant") ;
          assert (ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is invalid");
          return inf ;
      }

      if (mark == markOopDesc::INFLATING()) {
		  //如果是膨脹中,自旋,等待膨脹完成
         TEVENT (Inflate: spin while INFLATING) ;
         ReadStableMark(object) ;
         continue ;
      }

      if (mark->has_locker()) {
          ObjectMonitor * m = omAlloc (Self) ;
          // Optimistically prepare the objectmonitor - anticipate successful CAS
          // We do this before the CAS in order to minimize the length of time
          // in which INFLATING appears in the mark.
		  
		  //如果當前是輕量級鎖,獲取自旋次數,驗證是否應該升級為重量級鎖
          m->Recycle();
          m->FreeNext      = NULL ;
          m->_Responsible  = NULL ;
          m->OwnerIsThread = 0 ;
          m->_recursions   = 0 ;
          m->_SpinDuration = Knob_SpinLimit ;   // Consider: maintain by type/class

          markOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ;
          if (cmp != mark) {
             omRelease (Self, m) ;
             continue ;       // Interference -- just retry
          }

          
          markOop dmw = mark->displaced_mark_helper() ;
          assert (dmw->is_neutral(), "invariant") ;

          // Setup monitor fields to proper values -- prepare the monitor
          m->set_header(dmw) ;

          // Optimization: if the mark->locker stack address is associated
          // with this thread we could simply set m->_owner = Self and
          // m->OwnerIsThread = 1.  Note that a thread can inflate an object
          // that it has stack-locked -- as might happen in wait() -- directly
          // with CAS.  That is, we can avoid the xchg-NULL .... ST idiom.
          m->set_owner (mark->locker());
          m->set_object(object);
          // TODO-FIXME: assert BasicLock->dhw != 0.

          // Must preserve store ordering. The monitor state must
          // be stable at the time of publishing the monitor address.
          guarantee (object->mark() == markOopDesc::INFLATING(), "invariant") ;
          object->release_set_mark(markOopDesc::encode(m));

          // Hopefully the performance counters are allocated on distinct cache lines
          // to avoid false sharing on MP systems ...
          if (_sync_Inflations != NULL) _sync_Inflations->inc() ;
          TEVENT(Inflate: overwrite stacklock) ;
          if (TraceMonitorInflation) {
            if (object->is_instance()) {
              ResourceMark rm;
              tty->print_cr("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s",
                (intptr_t) object, (intptr_t) object->mark(),
                Klass::cast(object->klass())->external_name());
            }
          }
          return m ;
      }

      ...
}
程式碼只能是勉強在看,而且結合了他人的部落格的理解,但是大致能看懂一下它在做什麼操作。


重量級鎖的獲取和釋放,暫時沒有看懂,等後續看懂了再來書寫自己的理解吧

相關推薦

Java---偏向輕量級、自旋重量級

center jdk1 startup 例如 單元 cati 保護 讀鎖 text 之前做過一個測試,反復執行過多次,發現結果是一樣的: 1. 單線程下synchronized效率最高(當時感覺它的效率應該是最差才對); 2. AtomicInteger效率最不穩定,不同並

java 偏向輕量級重量級synchronized原理

bubuko 固定 使用情況 防止 不能 image 過程 記錄 原因 Java對象頭與Monitor java對象頭是實現synchronized的鎖對象的基礎,synchronized使用的鎖對象是存儲在Java對象頭裏的。 對象頭包含兩部分:Mark Word 和

Java偏向\輕量級\重量級總結

  資源消耗 目的 場景 實現方式 偏向鎖 一個執行緒只有一次CAS 單執行緒進行同步塊時,消除輕量級鎖的CAS操作。 大多數場景為單執

java 執行緒——偏向&輕量級&重量級

偏向鎖 輕量級鎖 重量級鎖 執行緒阻塞的代價 java的執行緒是對映到作業系統原生執行緒之上的,如果要阻塞或喚醒一個執行緒就需要作業系統介入,需要在戶態與核心態之間切換,這種切換會消耗大量的系統資源。 我們所熟知的Synchronized 在競爭鎖失

java 中的 -- 偏向輕量級、自旋重量級

之前做過一個測試,詳情見這篇文章《多執行緒 +1操作的幾種實現方式,及效率對比》,當時對這個測試結果很疑惑,反覆執行過多次,發現結果是一樣的:  1. 單執行緒下synchronized效率最高(當時感覺它的效率應該是最差才對);  2. AtomicInteger效率最不穩

java同步優化方案學習筆記(偏向輕量級,自旋重量級

目錄 一,概述 二,CAS 一,概述 什麼是java的鎖? 為什麼java要有鎖? java的鎖為什麼需要優化? 怎麼優化的? 1,java中使用synchronized關鍵字來實現同步功能,被synchronized修飾的方法

偏向輕量級重量級java

輕量級鎖是JDK 1.6之中加入的新型鎖機制,它名字中的“輕量級”是相對於使用作業系統互斥量來實現的傳統鎖而言的,因此傳統的鎖機制就稱為“重量級”鎖。首先需要強調一點的是,輕量級鎖並不是用來代替重量級鎖的,它的本意是在沒有多執行緒競爭的前提下,減少傳統的重量級鎖使用作業系統互

淺談Java裡的三種偏向輕量級重量級

在學習sychronized關鍵字及其實現細節的時候,發現java中的三種鎖,偏向鎖,輕量級鎖,重量級鎖其實也有很多值得探究的地方,引入偏向鎖是為了在無多執行緒競爭的情況下儘量減少不必要的輕量級鎖執行

【轉】Java -- 偏向輕量級、自旋重量級

之前做過一個測試,詳情見這篇文章《多執行緒 +1操作的幾種實現方式,及效率對比》,當時對這個測試結果很疑惑,反覆執行過多次,發現結果是一樣的:  1. 單執行緒下synchronized效率最高(當時感覺它的效率應該是最差才對);  2. AtomicInteger效率最不穩定,

Java中的偏向輕量級重量級解析

參考文章 Java 中的鎖 在 Java 中主要2種加鎖機制: synchronized 關鍵字 java.util.concurrent.Lock (Lock是一個介面,ReentrantLock是該介面一個很常用的實現) 這兩種機制的底層原理存在一定

Java--偏向/輕量級/重量級

首先要解釋清楚這幾種鎖的特徵和區別,以及執行時候的變化,需要了解Java物件頭的一些知識,在Java物件頭的Mark Word內就記錄著這些不同鎖的狀態位。另外偏向鎖---輕量級鎖---重量級鎖本文都需要依託synchronize進行理解和分析。另外也要參照網路上很多的資料。

Java面試--偏向、自旋輕量級重量級

它有多個佇列,當多個執行緒一起訪問某個物件監視器的時候,物件監視器會將這些執行緒儲存在不同的容器中。 1、Contention List:競爭佇列,所有請求鎖的執行緒首先被放在這個競爭佇列中; 2、Entry List:Contention List中那些有資格成為候選資源的執行緒被移動到Entry Li

聊聊java 中的偏向輕量級重量級的介紹

重量級鎖:即為傳統鎖,是通過互斥量來實現程式碼同步,但是互斥量相對於CAS(原子性操作),要產生更多的效能消耗。輕量級鎖:輕量級鎖是通過CAS來實現同步,相對重量級鎖的互斥量,效能會好很多。但是輕量級鎖不是為了替代重量級鎖。                    輕量級鎖能夠

java的四種狀態 無狀態 偏向狀態 輕量級狀態 重量級狀態

一:java多執行緒互斥,和java多執行緒引入偏向鎖和輕量級鎖的原因?--->synchronized的重量級別的鎖,就是線上程執行到該程式碼塊的時候,讓程式的執行級別從使用者態切換到核心態,把所有的執行緒掛起,讓cpu通過作業系統指令,去排程多執行緒之間,誰執行程式

Java的升級策略 偏向 輕量級 重量級

這三種鎖是指鎖的狀態,並且是專門針對Synchronized關鍵字。JDK 1.6 為了減少"重量級鎖"的效能消耗,引入了“偏向鎖”和“輕量級鎖”,鎖一共擁有4種狀態:無鎖狀態、偏向鎖、輕量級鎖、重量級鎖。鎖狀態是通過物件頭的Mark Word來進行標記的: 鎖可以升級但不能降級,意味

java併發筆記之synchronized 偏向 輕量級 重量級證明

 警告⚠️:本文耗時很長,先做好心理準備 本篇將從hotspot原始碼(64 bits)入手,通過分析java物件頭引申出鎖的狀態;本文采用大量例項及分析,請耐心看完,謝謝   先來看一下hotspot的原始碼當中的物件頭的註釋(32bits 可以忽略了,現在基本沒有32位作業系

JAVA物件分析之偏向輕量級重量級升級過程

在HotSpot虛擬機器裡,物件在堆記憶體中的儲存佈局可以劃分為三個部分: 物件頭(Header) 例項資料(Instance Data) 對齊填充(Padding)。 ## 物件頭 HotSpot虛擬機器(後面沒有說明的話預設是這個虛擬機器)物件頭包括三部分: 1、Mark Word 2、

輕量級偏向重量級詳情

這篇文章是上篇文章是否真的理解了偏向鎖、輕量級鎖、重量級鎖(鎖膨脹)、自旋鎖、鎖消除、鎖粗化,知道重偏向嗎?的補充,對於偏向鎖,網上有些對於它的原理解讀過於簡單,簡單得似乎是錯誤的,最顯眼的是對於Mark Word的倒數第三位的作用的含義,許多部落格對於這個的作用搞成標誌是否使用偏向鎖,其實還有

偏向輕量級重量級膨脹)、自旋消除、粗化

知識準備: 在開始前,首先清楚系統PV訊號機制 荷蘭學者Dijkstra於1965年提出的訊號機制是一種有效的程序同步與互斥工具。 1)整型訊號與PV操作 訊號量是一個整型變數,根據控制物件的不同被賦予不同的值。訊號量分為如下兩類: (1)公用訊號量。實現程序間的互

偏向輕量級,自旋重量級的詳細介紹

何為同步?JVM規範規定JVM基於進入和退出Monitor物件來實現方法同步和程式碼塊同步,但兩者的實現細節不一樣。程式碼塊同步是使用monitorenter和monitorexit指令實現,而方法同步是使用另外一種方式實現的,細節在JVM規範裡並沒有詳細說明,但是方法的同步