真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

【并發(fā)】并發(fā)鎖機制-深入理解synchronized(二)-創(chuàng)新互聯(lián)

【并發(fā)】并發(fā)鎖機制-深入理解synchronized(二)

成都創(chuàng)新互聯(lián)是一家網(wǎng)站設(shè)計公司,集創(chuàng)意、互聯(lián)網(wǎng)應(yīng)用、軟件技術(shù)為一體的創(chuàng)意網(wǎng)站建設(shè)服務(wù)商,主營產(chǎn)品:響應(yīng)式網(wǎng)站設(shè)計品牌網(wǎng)站建設(shè)、網(wǎng)絡(luò)營銷推廣。我們專注企業(yè)品牌在網(wǎng)站中的整體樹立,網(wǎng)絡(luò)互動的體驗,以及在手機等移動端的優(yōu)質(zhì)呈現(xiàn)。做網(wǎng)站、網(wǎng)站制作、移動互聯(lián)產(chǎn)品、網(wǎng)絡(luò)運營、VI設(shè)計、云產(chǎn)品.運維為核心業(yè)務(wù)。為用戶提供一站式解決方案,我們深知市場的競爭激烈,認(rèn)真對待每位客戶,為客戶提供賞析悅目的作品,網(wǎng)站的價值服務(wù)。

synchronized 高級篇(底層原理)

一、查看synchronized的字節(jié)碼指令序列

同步方法

同步代碼塊?

二、Monitor(管程/監(jiān)視器)

MESA模型

wait()的正確使用姿勢

notify() 和 notifyAll() 分別何時使用

關(guān)于 wait、notify、notifyAll的問題詳解

Java語言的內(nèi)置管程synchronized

Monitor機制在Java中的實現(xiàn)

圖解Java中的Monitor機制

【思考】synchronized加鎖加在對象上,鎖對象是如何記錄鎖狀態(tài)的??

三、對象的內(nèi)存布局

【了解】什么是對象頭?

【問】new Object() 在對象中占用幾個字節(jié)???

四、使用JOL工具查看內(nèi)存布局

導(dǎo)pom依賴

示例代碼?

運行結(jié)果?

下一節(jié)——synchronized底層鎖的優(yōu)化解析


【并發(fā)】并發(fā)鎖機制-深入理解synchronized(二) synchronized 高級篇(底層原理)

synchronized是JVM內(nèi)置鎖,基于Monitor機制實現(xiàn)。

這個Monitor就是管程的意思,它可以控制線程,讓其陷入等待,或者將其喚醒!

synchronized 依賴底層操作系統(tǒng)的互斥原語Mutex(互斥量),它是一個重量級鎖,性能較低。

因為,有使用到操作系統(tǒng)底層的原語Mutex,我們只能通過系統(tǒng)調(diào)用來使用它!所以,CPU要從用戶態(tài)到內(nèi)核態(tài),它是一個很重的操作!

不過,在JVM內(nèi)置鎖在1.5之后版本做了重大的優(yōu)化,如鎖粗化(Lock Coarsening)、鎖消除(Lock Elimination)、輕量級鎖(Lightweight Locking)、偏向鎖(Biased Locking)、自適應(yīng)自旋(Adaptive Spinning)等技術(shù)來減少鎖操作的開銷,內(nèi)置鎖的并發(fā)性能已經(jīng)基本與Lock持平。?

根據(jù)一些測試報告,在數(shù)據(jù)量不是很大的情況下,synchronized的性能大約只比ReentrantLock 差10%-20%!?

一、查看synchronized的字節(jié)碼指令序列

Java虛擬機通過一個同步結(jié)構(gòu)支持方法和方法中的指令序列的同步:monitor

同步方法是通過方法中的access_flags(訪問標(biāo)志位)中設(shè)置ACC_SYNCHRONIZED標(biāo)志來實現(xiàn)。

同步代碼塊是通過?monitorenter?和?monitorexit?來實現(xiàn)。兩個指令的執(zhí)行是JVM通過調(diào)用操作系統(tǒng)的互斥原語mutex來實現(xiàn),被阻塞的線程會被掛起、等待重新調(diào)度,會導(dǎo)致“用戶態(tài)和內(nèi)核態(tài)”兩個態(tài)之間來回切換,對性能有較大影響。

同步方法
private static int counter = 0;

public synchronized static void increment() {
    counter++;
}
public synchronized static void decrement() {
    counter--;
}

這里的synchronized加在方法上面,所以方法內(nèi)部的指令沒有發(fā)生變化!僅僅是加了一個標(biāo)志位!

這邊顯示的是0x0029,其實是0x0001 + 0x0008+ 0x0020

同步代碼塊?
private static String lock = "";

public static void increment() {
    synchronized (lock) {
        counter++;
    }
}

public static void decrement() {
    synchronized (lock) {
        counter--;
    }
}

這里方法內(nèi)部的指令發(fā)生的改變!

【問】為什么monitorexit指令有2次??

第一個monitorexit指令是同步代碼塊正常釋放鎖的一個標(biāo)志

如果同步代碼塊中出現(xiàn)Exception或者Error,則會調(diào)用第二個monitorexit指令來保證釋放鎖

二、Monitor(管程/監(jiān)視器)

Monitor在操作系統(tǒng)中就是管程,而在Java中,我們通常稱它為監(jiān)視器!

管程是指管理共享變量以及對共享變量操作的過程,讓它們支持并發(fā)。

在Java 1.5之前,Java語言提供的唯一并發(fā)語言就是管程,Java 1.5之后提供的SDK并發(fā)包也是以管程為基礎(chǔ)的!例如:JUC

synchronized關(guān)鍵字和wait()、notify()、notifyAll()這三個方法是Java中實現(xiàn)管程技術(shù)的組成部分。

MESA模型

在管程的發(fā)展史上,先后出現(xiàn)過三種不同的管程模型——Hasen模型、Hoare模型和MESA模型。

現(xiàn)在正在廣泛使用的是MESA模型,介紹如下:

入口只允許一個線程通過,其余的現(xiàn)在入口等待隊列中等待!這樣子設(shè)計可以解決互斥的問題!?

進去之后,里面還提供了條件變量,每個條件變量都對應(yīng)有一個等待隊列!

條件變量和其等待隊列的作用是解決線程之間的同步問題!條件隊列里面存的東西,可以理解為“被wait()” 的線程。

wait()的正確使用姿勢

對于MESA管程來說,有一個編程范式:

while(條件不滿足) {
??wait();
}

喚醒的時間和獲取到鎖繼續(xù)執(zhí)行的時間是不一致的,被喚醒的線程再次執(zhí)行時可能條件又不滿足了,所以循環(huán)檢驗條件。MESA模型的wait()方法還有一個超時參數(shù),為了避免線程進入等待隊列永久阻塞。?

我們可以看看Object類里面的對于?wait() 方法的注解描述:

確實需要將其放在循環(huán)里面。?

notify() 和 notifyAll() 分別何時使用

滿足以下三個條件時,可以使用notify(),其余情況盡量使用notifyAll():

  1. 所有等待線程擁有相同的等待條件;
  2. 所有等待線程被喚醒后,執(zhí)行相同的操作;
  3. 只需要喚醒一個線程。
關(guān)于 wait、notify、notifyAll的問題詳解

【面試題】notify() 和 notifyAll()方法的使用和區(qū)別_面向架構(gòu)編程的博客-博客https://blog.csdn.net/weixin_43715214/article/details/128665586

Java語言的內(nèi)置管程synchronized

Java 參考了 MESA 模型,語言內(nèi)置的管程(synchronized)對 MESA 模型進行了精簡。

MESA 模型中,條件變量可以有多個,Java 語言內(nèi)置的管程里只有一個條件變量。

模型如下圖所示:

Monitor機制在Java中的實現(xiàn)

java.lang.Object 類定義了 wait(),notify(),notifyAll() 方法,這些方法的具體實現(xiàn),依賴于 ObjectMonitor 實現(xiàn),這是 JVM 內(nèi)部基于 C++ 實現(xiàn)的一套機制。

ObjectMonitor?其主要數(shù)據(jù)結(jié)構(gòu)如下(hotspot源碼ObjectMonitor.hpp):

ObjectMonitor() {
    _header       = NULL; //對象頭  markOop
    _count        = 0;  
    _waiters      = 0,   
    _recursions   = 0;   // 鎖的重入次數(shù) 
    _object       = NULL;  //存儲鎖對象
    _owner        = NULL;  // 標(biāo)識擁有該monitor的線程(當(dāng)前獲取鎖的線程) 
    _WaitSet      = NULL;  // 等待線程(調(diào)用wait)組成的雙向循環(huán)鏈表,_WaitSet是第一個節(jié)點
    _WaitSetLock  = 0 ;    
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ; //多線程競爭鎖會先存到這個單向鏈表中 (FILO棧結(jié)構(gòu))
    FreeNext      = NULL ;
    _EntryList    = NULL ; //存放在進入或重新進入時被阻塞(blocked)的線程 (也是存競爭鎖失敗的線程)
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;
}

其中涉及到3種鏈表——cxq、WaitSet、EntryList

但是從隊列中挑選一個線程進行喚醒,如何挑選?有沒有什么原則?

這一方面比較復(fù)雜!具體看下文?。?!

圖解Java中的Monitor機制

首先,所有線程去競爭鎖,競爭失敗的線程會進入cxq(FILO)里面;

然后,持有鎖的線程,執(zhí)行后續(xù)邏輯,如果有?wait?方法,進入等待隊列waitSet;

接下來,可能繼續(xù)被喚醒,可能會進入cxq隊列(棧),也可能進入EntryList(這要看具體的策略?。?/p>

最后,再次競爭鎖的時候,可能從cxq中獲取,也可能從EntryList中獲?。?

默認(rèn)策略

如果?EntryList為空,則將?cxq中的元素按原有順序插入到EntryList,并喚醒第一個線程,也就是當(dāng)EntryList為空時,是后來的線程先獲取鎖。(非公平?。?/p>

如果 EntryList不為空,直接從 EntryList 中喚醒線程。

【思考】synchronized加鎖加在對象上,鎖對象是如何記錄鎖狀態(tài)的??

鎖狀態(tài)是被記錄在每個對象的對象頭(Mark Word)中!具體看下文!

三、對象的內(nèi)存布局

Hotspot虛擬機中,對象在內(nèi)存中存儲的布局可以分為三塊區(qū)域:對象頭(Header)、實例數(shù)據(jù)(Instance Data)和?對齊填充(Padding)

  • 對象頭:比如 hash碼,對象所屬的年代,對象鎖,鎖狀態(tài)標(biāo)志,偏向鎖(線程)ID,偏向時間,數(shù)組長度(數(shù)組對象才有)等。
  • 實例數(shù)據(jù):存放類的屬性數(shù)據(jù)信息,包括父類的屬性信息;
  • 對齊填充:由于虛擬機要求?對象起始地址 必須是8字節(jié)的整數(shù)倍。填充數(shù)據(jù)不是必須存在的,僅僅是為了字節(jié)對齊。

【了解】什么是對象頭?

對象頭是對象中最復(fù)雜的部分!HotSpot虛擬機的對象頭包括:3個部分

(1)Mark Word?

用于存儲對象自身的運行時數(shù)據(jù),如哈希碼(HashCode)、GC分代年齡、鎖狀態(tài)標(biāo)志、線程持有的鎖、偏向線程ID、偏向時間戳等,這部分?jǐn)?shù)據(jù)的長度在32位和64位的虛擬機中分別為32bit和64bit,官方稱它為“Mark Word”。

圖解32位的JVM的存儲情況

圖解64位的JVM的存儲情況

雖然它們在不同位數(shù)的JVM中長度不一樣,但是基本組成內(nèi)容是一致的。

  • 鎖標(biāo)志位(lock):區(qū)分鎖狀態(tài),11時表示對象待GC回收狀態(tài), 只有最后2位鎖標(biāo)識(11)有效。
  • biased_lock:是否偏向鎖,由于無鎖和偏向鎖的鎖標(biāo)識都是 01,沒辦法區(qū)分,這里引入一位的偏向鎖標(biāo)識位。
  • 分代年齡(age):表示對象被GC的次數(shù),當(dāng)該次數(shù)到達閾值的時候,對象就會轉(zhuǎn)移到老年代。大是15,所以是四位!
  • 對象的hashcode(hash):運行期間調(diào)用System.identityHashCode()來計算,延遲計算,并把結(jié)果賦值到這里。當(dāng)對象加鎖后,計算的結(jié)果31位不夠表示,在偏向鎖,輕量鎖,重量鎖,hashcode會被轉(zhuǎn)移到Monitor中。
  • 偏向鎖的線程ID(JavaThread):偏向模式的時候,當(dāng)某個線程持有對象的時候,對象這里就會被置為該線程的ID。 在后面的操作中,就無需再進行嘗試獲取鎖的動作。
  • epoch:偏向鎖在CAS鎖操作過程中,偏向性標(biāo)識,表示對象更偏向哪個鎖
  • ptr_to_lock_record:輕量級鎖狀態(tài)下,指向棧中鎖記錄的指針。當(dāng)鎖獲取是無競爭的時,JVM使用原子操作而不是OS互斥。這種技術(shù)稱為輕量級鎖定。在輕量級鎖定的情況下,JVM通過CAS操作在對象的標(biāo)題字中設(shè)置指向鎖記錄的指針。
  • ptr_to_heavyweight_monitor:重量級鎖狀態(tài)下,指向?qū)ο蟊O(jiān)視器Monitor的指針。如果兩個不同的線程同時在同一個對象上競爭,則必須將輕量級鎖定升級到Monitor以管理等待的線程。在重量級鎖定的情況下,JVM在對象的ptr_to_heavyweight_monitor設(shè)置指向Monitor的指針。

簡單的來說就是:

enum { 
    locked_value             = 0,    //00 輕量級鎖 
    unlocked_value           = 1,    //001 無鎖
    monitor_value            = 2,    //10 監(jiān)視器鎖,也叫膨脹鎖,也叫重量級鎖
    marked_value             = 3,    //11 GC標(biāo)記
    biased_lock_pattern      = 5     //101 偏向鎖
}

圖示如下:

(2)Klass Pointer

對象頭的另外一部分是klass類型指針,即對象指向它的類元數(shù)據(jù)的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。默認(rèn)開啟壓縮指針,4個字節(jié)(未開啟是8個)

(3)數(shù)組長度(只有數(shù)組對象有)

如果對象是一個數(shù)組, 那在對象頭中還必須有一塊數(shù)據(jù)用于記錄數(shù)組長度。?4字節(jié)

【問】new Object() 在對象中占用幾個字節(jié)???

JDK8是默認(rèn)開啟壓縮指針,如果該對象是數(shù)組,則對象頭是16個字節(jié);反之,只是一個普通對象,則對象頭是12個字節(jié)

但是請注意,對于 new Object() 來說,首先它不是數(shù)組,則對象頭為12個字節(jié),其次它是一個空對象,則它的實例數(shù)據(jù)為0,但是一個對象所占的字節(jié)必須是8個字節(jié)的整數(shù)倍,所以對齊填充位要補4個字節(jié)!加起來一共是16個字節(jié)!

我們可以用JOL來佐證!詳情如下:?

public class ObjectTest {
    public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());
    }
}

四、使用JOL工具查看內(nèi)存布局

查看普通 java對象的內(nèi)部布局工具JOL(JAVA OBJECT LAYOUT),使用此工具可以查看 new 出來的一個 java對象的內(nèi)部布局,以及一個普通的java對象占用多少字節(jié)。?

導(dǎo)pom依賴
org.openjdk.joljol-core0.10
示例代碼?
public class ObjectTest {
    public static void main(String[] args) throws InterruptedException {
        Object obj = new Test();

        //查看對象內(nèi)部信息
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());
    }
}

class Test{
    private boolean flag;
    private long  p;
}
運行結(jié)果?

下一節(jié)——synchronized底層鎖的優(yōu)化解析

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧


本文標(biāo)題:【并發(fā)】并發(fā)鎖機制-深入理解synchronized(二)-創(chuàng)新互聯(lián)
本文鏈接:http://weahome.cn/article/dpdjpj.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部