欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java?synchronized底層實現(xiàn)原理以及鎖優(yōu)化

 更新時間:2022年02月07日 10:12:02   作者:Medlen  
Synchronized是Java中解決并發(fā)問題的一種最常用的方法,也是最簡單的一種方法,下面這篇文章主要給大家介紹了關于Java?synchronized底層實現(xiàn)原理以及鎖優(yōu)化的相關資料,需要的朋友可以參考下

一、概述

synchronized簡介

在多線程并發(fā)編程中 synchronized 一直是元老級角色,很多人都會稱呼它為重量級鎖。但是,隨著 Java SE 1.6 對synchronized 進行了各種優(yōu)化之后,有些情況下它就并不那么重,Java SE 1.6 中為了減少獲得鎖和釋放鎖帶來的性能消耗而引入的偏向鎖和輕量級鎖。這塊在后續(xù)介紹中會慢慢引入。

synchronized的基本語法

synchronized 有三種方式來加鎖,分別是

  • 修飾實例方法,作用于當前實例加鎖,進入同步代碼前要獲得當前實例的鎖
  • 靜態(tài)方法,作用于當前類對象加鎖,進入同步代碼前要獲得當前類對象的鎖
  • 修飾代碼塊,指定加鎖對象,對給定對象加鎖,進入同步代碼庫前要獲得給定對象的鎖。

synchronized作用

  • 原子性:synchronized保證語句塊內操作是原子的
  • 可見性:synchronized保證可見性(通過“在執(zhí)行unlock之前,必須先把此變量同步回主內存”實現(xiàn))
  • 有序性:synchronized保證有序性(通過“一個變量在同一時刻只允許一條線程對其進行l(wèi)ock操作”)

synchronized的使用

  • 修飾實例方法,對當前實例對象加鎖
  • 修飾靜態(tài)方法,多當前類的Class對象加鎖
  • 修飾代碼塊,對synchronized括號內的對象加鎖

二、實現(xiàn)原理

1、jvm基于進入和退出Monitor對象來實現(xiàn)方法同步和代碼塊同步。

方法級的同步是隱式,即無需通過字節(jié)碼指令來控制的,它實現(xiàn)在方法調用和返回操作之中。JVM可以從方法常量池中的方法表結構(method_info Structure) 中的 ACC_SYNCHRONIZED 訪問標志區(qū)分一個方法是否同步方法。當方法調用時,調用指令將會 檢查方法的 ACC_SYNCHRONIZED 訪問標志是否被設置,如果設置了,執(zhí)行線程將先持有monitor(虛擬機規(guī)范中用的是管程一詞), 然后再執(zhí)行方法,最后再方法完成(無論是正常完成還是非正常完成)時釋放monitor。

代碼塊的同步是利用monitorenter和monitorexit這兩個字節(jié)碼指令。它們分別位于同步代碼塊的開始和結束位置。當jvm執(zhí)行到monitorenter指令時,當前線程試圖獲取monitor對象的所有權,如果未加鎖或者已經被當前線程所持有,就把鎖的計數(shù)器+1;當執(zhí)行monitorexit指令時,鎖計數(shù)器-1;當鎖計數(shù)器為0時,該鎖就被釋放了。如果獲取monitor對象失敗,該線程則會進入阻塞狀態(tài),直到其他線程釋放鎖。

這里要注意:

  • synchronized是可重入的,所以不會自己把,自己鎖死
  • synchronized鎖一旦被一個線程持有,其他試圖獲取該鎖的線程將被阻塞。

關于ACC_SYNCHRONIZED 、monitorenter、monitorexit指令,可以看一下下面的反編譯代碼:

public class SynchronizedDemo {
    public synchronized void f(){    //這個是同步方法
        System.out.println("Hello world");
    }
    public void g(){
        synchronized (this){		//這個是同步代碼塊
            System.out.println("Hello world");
        }
    }
    public static void main(String[] args) {

    }
}

使用javap -verbose SynchronizedDemo反編譯后得到

我們看到對于同步方法,反編譯后得到ACC_SYNCHRONIZED 標志,對于同步代碼塊反編譯后得到monitorenter和monitorexit指令。

三、理解Java對象頭

在JVM中,對象在內存中的布局分為三塊區(qū)域:對象頭、實例數(shù)據(jù)和對齊填充。

實例變量:存放類的屬性數(shù)據(jù)信息,包括父類的屬性信息,如果是數(shù)組的實例部分還包括數(shù)組的長度,這部分內存按4字節(jié)對齊。

填充數(shù)據(jù):由于虛擬機要求對象起始地址必須是8字節(jié)的整數(shù)倍。填充數(shù)據(jù)不是必須存在的,僅僅是為了字節(jié)對齊。

HotSpot虛擬機的對象頭分為兩部分信息,第一部分用于存儲對象自身運行時數(shù)據(jù),如哈希碼、GC分代年齡等,這部分數(shù)據(jù)的長度在32位和64位的虛擬機中分別為32位和64位。官方稱為Mark Word。另一部分用于存儲指向對象類型數(shù)據(jù)的指針,如果是數(shù)組對象的話,還會有一個額外的部分存儲數(shù)組長度。

虛擬機位數(shù)對象頭結構描述
32位/64位Mark Word存儲對象的哈希碼、GC分代年齡、鎖信息等
32位/64位Class MetaData Address指向對象類型數(shù)據(jù)的指針
32位/64位數(shù)組長度如果是數(shù)組對象的話,有這一部分,否則沒有

由于對象頭的信息是與對象自身定義的數(shù)據(jù)沒有關系的額外存儲成本,因此考慮到JVM的空間效率,Mark Word 被設計成為一個非固定的數(shù)據(jù)結構,以便存儲更多有效的數(shù)據(jù),它會根據(jù)對象本身的狀態(tài)復用自己的存儲空間。

四、JVM對synchronized的鎖優(yōu)化

Synchronized是通過對象內部的一個叫做監(jiān)視器鎖(monitor)來實現(xiàn)的,監(jiān)視器鎖本質又是依賴于底層的操作系統(tǒng)的Mutex Lock(互斥鎖)來實現(xiàn)的。而操作系統(tǒng)實現(xiàn)線程之間的切換需要從用戶態(tài)轉換到核心態(tài),這個成本非常高,狀態(tài)之間的轉換需要相對比較長的時間,這就是為什么Synchronized效率低的原因。因此,這種依賴于操作系統(tǒng)Mutex Lock所實現(xiàn)的鎖我們稱之為“重量級鎖”。

Java SE 1.6為了減少獲得鎖和釋放鎖帶來的性能消耗,引入了“偏向鎖”和“輕量級鎖”:鎖一共有4種狀態(tài),級別從低到高依次是:無鎖狀態(tài)、偏向鎖狀態(tài)、輕量級鎖狀態(tài)和重量級鎖狀態(tài)。鎖可以升級但不能降級。

1、偏向鎖

偏向鎖是JDK1.6中引用的優(yōu)化,它的目的是消除數(shù)據(jù)在無競爭情況下的同步原語,進一步提高程序的性能。

偏向鎖的獲?。?/p>

  • 判斷是否為可偏向狀態(tài)
  • 如果為可偏向狀態(tài),則判斷線程ID是否是當前線程,如果是進入同步塊;
  • 如果線程ID并未指向當前線程,利用CAS操作競爭鎖,如果競爭成功,將Mark Word中線程ID更新為當前線程ID,進入同步塊
  • 如果競爭失敗,等待全局安全點,準備撤銷偏向鎖,根據(jù)線程是否處于活動狀態(tài),決定是轉換為無鎖狀態(tài)還是升級為輕量級鎖。

當鎖對象第一次被線程獲取的時候,虛擬機會把對象頭中的標志位設置為“01”,即偏向模式。同時使用CAS操作把獲取到這個鎖的線程ID記錄在對象的Mark Word中,如果CAS操作成功。持有偏向鎖的線程以后每次進入這個鎖相關的同步塊時,虛擬機都可以不再進行任何同步操作。

偏向鎖的釋放:

偏向鎖使用了遇到競爭才釋放鎖的機制。偏向鎖的撤銷需要等待全局安全點,然后它會首先暫停擁有偏向鎖的線程,然后判斷線程是否還活著,如果線程還活著,則升級為輕量級鎖,否則,將鎖設置為無鎖狀態(tài)。

2、輕量級鎖

輕量級鎖也是在JDK1.6中引入的新型鎖機制。它不是用來替換重量級鎖的,它的本意是在沒有多線程競爭的情況下,減少傳統(tǒng)的重量級鎖使用操作系統(tǒng)互斥量產生的性能消耗。

加鎖過程:

在代碼進入同步塊的時候,如果此對象沒有被鎖定(鎖標志位為“01”狀態(tài)),虛擬機首先在當前線程的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用于存儲對象目前Mark Word的拷貝(官方把這份拷貝加了一個Displaced前綴,即Displaced Mark Word)。然后虛擬機使用CAS操作嘗試將對象的Mark Word更新為指向鎖記錄(Lock Record)的指針。如果更新成功,那么這個線程就擁有了該對象的鎖,并且對象的Mark Word標志位轉變?yōu)?ldquo;00”,即表示此對象處于輕量級鎖定狀態(tài);如果更新失敗,虛擬機首先會檢查對象的Mark Word是否指向當前線程的棧幀,如果說明當前線程已經擁有了這個對象的鎖,那就可以直接進入同步塊中執(zhí)行,否則說明這個鎖對象已經被其他線程占有了。如果有兩條以上的線程競爭同一個鎖,那輕量級鎖不再有效,要膨脹為重量級鎖,鎖標志變?yōu)?ldquo;10”,Mark Word中存儲的就是指向重量級鎖的指針,而后面等待的線程也要進入阻塞狀態(tài)。

解鎖過程:

如果對象的Mark Word仍然指向線程的鎖記錄,那就用CAS操作將對象當前的Mark Word與線程棧幀中的Displaced Mark Word交換回來,如果替換成功,整個同步過程就完成了。如果替換失敗,說明有其他線程嘗試過獲取該鎖,那就要在釋放鎖的同時,喚醒被掛起的線程。

如果沒有競爭,輕量級鎖使用CAS操作避免了使用互斥量的開銷,但如果存在競爭,除了互斥量的開銷外,還額外發(fā)生了CAS操作,因此在有競爭的情況下,輕量級鎖比傳統(tǒng)重量級鎖開銷更大。

3、重量級鎖

Synchronized的重量級鎖是通過對象內部的一個叫做監(jiān)視器鎖(monitor)來實現(xiàn)的,監(jiān)視器鎖本質又是依賴于底層的操作系統(tǒng)的Mutex Lock(互斥鎖)來實現(xiàn)的。而操作系統(tǒng)實現(xiàn)線程之間的切換需要從用戶態(tài)轉換到核心態(tài),這個成本非常高,狀態(tài)之間的轉換需要相對比較長的時間,這就是為什么Synchronized效率低的原因。

4、自旋鎖

互斥同步對性能影響最大的是阻塞的實現(xiàn),掛起線程和恢復線程的操作都需要轉入到內核態(tài)中完成,這些操作給系統(tǒng)的并發(fā)性能帶來很大的壓力。
于是在阻塞之前,我們讓線程執(zhí)行一個忙循環(huán)(自旋),看看持有鎖的線程是否釋放鎖,如果很快釋放鎖,則沒有必要進行阻塞。

5、鎖消除

鎖消除是指虛擬機即時編譯器(JIT)在運行時,對一些代碼上要求同步,但是檢測到不可能發(fā)生數(shù)據(jù)競爭的鎖進行消除。

6、鎖粗化

如果虛擬機檢測到有這樣一串零碎的操作都對同一個對象加鎖,將會把加鎖同步的范圍擴展(粗化)到整個操作序列的外部。

參考:深入理解Java并發(fā)之synchronized實現(xiàn)原理
參考:Java的對象頭和對象組成詳解

總結

到此這篇關于Java synchronized底層實現(xiàn)原理以及鎖優(yōu)化的文章就介紹到這了,更多相關synchronized底層實現(xiàn)原理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • java中進制的轉換,Byte與16進制的轉換方法

    java中進制的轉換,Byte與16進制的轉換方法

    下面小編就為大家?guī)硪黄猨ava中進制的轉換,Byte與16進制的轉換方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-11-11
  • 兩種Eclipse部署動態(tài)web項目方法

    兩種Eclipse部署動態(tài)web項目方法

    這篇文章主要介紹了兩種Eclipse部署動態(tài)web項目方法,需要的朋友可以參考下
    2015-11-11
  • Spring Boot應用的極速部署腳本示例代碼

    Spring Boot應用的極速部署腳本示例代碼

    最近在工作中遇到了一個問題,需要極速的部署Spring Boot應用,發(fā)現(xiàn)網上這方面的資料較少,所以自己來總結下,這篇文章主要給大家介紹了關于Spring Boot應用的極速部署腳本的相關資料,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-08-08
  • Java微信公眾平臺開發(fā)(3) 接收消息的分類及實體的創(chuàng)建

    Java微信公眾平臺開發(fā)(3) 接收消息的分類及實體的創(chuàng)建

    這篇文章主要為大家詳細介紹了Java微信公眾平臺開發(fā)第三步,接收消息的分類及實體的創(chuàng)建,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • jasypt SaltGenerator接口定義方法源碼解讀

    jasypt SaltGenerator接口定義方法源碼解讀

    這篇文章主要為大家介紹了jasypt SaltGenerator接口定義方法源碼解讀,,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09
  • Java輸出打印工具類封裝的實例

    Java輸出打印工具類封裝的實例

    下面小編就為大家?guī)硪黄狫ava輸出打印工具類封裝的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • java中?${}?和?#{}?有什么區(qū)別

    java中?${}?和?#{}?有什么區(qū)別

    本文主要介紹了java中${}和#{}有什么區(qū)別,${}和#{}都是MyBatis中用來替換參數(shù)的,它們都可以將用戶傳遞過來的參數(shù),替換到MyBatis最終生成的SQL中,但它們區(qū)別卻是很大的,感興趣的小伙伴可以一起來學習下面詳細內容
    2022-08-08
  • String字符串拼接方法concat和+的效率對比

    String字符串拼接方法concat和+的效率對比

    這篇文章主要介紹了String字符串拼接方法concat和+的效率對比,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Springboot詳解實現(xiàn)食品倉庫管理系統(tǒng)流程

    Springboot詳解實現(xiàn)食品倉庫管理系統(tǒng)流程

    這是一個使用Springboot開發(fā)的食品倉庫管理系統(tǒng),是為商家提供商品貨物進銷存的信息化管理系統(tǒng),具有一個倉庫管理系統(tǒng)該有的所有功能,感興趣的朋友快來看看吧
    2022-06-06
  • 使用java + selenium + OpenCV破解騰訊防水墻滑動驗證碼功能

    使用java + selenium + OpenCV破解騰訊防水墻滑動驗證碼功能

    這篇文章主要介紹了使用java + selenium + OpenCV破解騰訊防水墻滑動驗證碼,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11

最新評論