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

Java中synchronized鎖升級(jí)的過(guò)程

 更新時(shí)間:2022年05月17日 08:48:55   作者:??呆呆燦????  
本文主要介紹了Java中synchronized鎖升級(jí)的過(guò)程,synchronized相對(duì)于早期的synchronized做出了優(yōu)化,從以前的加鎖就是重量級(jí)鎖優(yōu)化成了有一個(gè)鎖升級(jí)的過(guò),下文詳細(xì)內(nèi)容需要的小伙伴可以參考一下

簡(jiǎn)介

在多線程中解決線程安全的問(wèn)題時(shí)常用到Synchronized,現(xiàn)在的synchronized相對(duì)于早期的synchronized做出了優(yōu)化,從以前的加鎖就是重量級(jí)鎖優(yōu)化成了有一個(gè)鎖升級(jí)的過(guò)程(偏向鎖->輕量級(jí)鎖->重量級(jí)鎖)。

CAS

cas的全稱是compare and swap,從名稱上可以看出它是先比較再進(jìn)行設(shè)置,它是一種在多線程環(huán)境下實(shí)現(xiàn)同步功能的機(jī)制。

下面這段代碼是在ReentrantLock類中復(fù)制的一段關(guān)于CAS操作的代碼

protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

compareAndSwapInt的參數(shù),這里的參數(shù)一和參數(shù)二現(xiàn)在把他理解成是一個(gè)參數(shù)unsafe.compareAndSwapInt(curr, expect, update);所以這一個(gè)Cas操作里面需要三個(gè)參數(shù)

  • 參數(shù)一:當(dāng)前值
  • 參數(shù)二:期望值
  • 參數(shù)三:需要修改成的值

只有在當(dāng)前值和期望值一致的時(shí)候才會(huì)將當(dāng)前值修改成參數(shù)三所傳入的值。

CAS在JUC包中應(yīng)用很廣泛,比如在AtomicXXX類中使用到了大量的CAS操作,

CAS不是很難理解,有個(gè)概念就好。

markWord

如果了解對(duì)象的內(nèi)存布局的可以略過(guò)此段。這個(gè)對(duì)象的內(nèi)存布局是和JVM的實(shí)現(xiàn)有關(guān),本章所說(shuō)的是HotSpot的實(shí)現(xiàn)。

當(dāng)一個(gè)對(duì)象被創(chuàng)建出來(lái)后它在內(nèi)存中的布局如下,由四部分組成:

  • 8個(gè)字節(jié)的markword,(markword里面包含了其它的東西,比如GC標(biāo)記,鎖類型)
  • 4個(gè)字節(jié)的ClassPoint(此指針指向的Class),默認(rèn)是開(kāi)啟指針壓縮所以是四個(gè)字節(jié),關(guān)閉指針壓縮后是八個(gè)字節(jié)
  • 實(shí)例對(duì)象中的成員屬性大小
  • 字節(jié)填充(有的JVM需要8字節(jié)對(duì)齊,如果上面的字節(jié)相加后不能被8整除,則需要在此補(bǔ)齊)

看到上面的圖,應(yīng)該可以大概的看出來(lái)synchronized加鎖,其實(shí)就是修改的對(duì)像頭里面的markword的數(shù)據(jù)。所以synchronized可以對(duì)任何一個(gè)對(duì)象加鎖

現(xiàn)在有一個(gè)Java類T,將它new出來(lái)之后它的對(duì)象的內(nèi)存布局是什么樣子的呢?

class T{
    Integer age;
}

可以通過(guò)一個(gè)小工具來(lái)查看下這個(gè)T類在內(nèi)存中的對(duì)象布局

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
    <scope>compile</scope>
</dependency>

通過(guò)下面的程序來(lái)打印下T對(duì)象的布局是什么樣子的。

public static void main(String[] args) {
    T o = new T();
    System.out.println(ClassLayout.parseInstance(o).toPrintable());
}

這張圖是一個(gè)沒(méi)有加鎖的對(duì)象的對(duì)象布局。

通過(guò)synchronized后的對(duì)象布局是什么樣子的呢?這次再修改下T類,目的是讓它存在字節(jié)填充

class T{
    Integer age;
    Integer age1;
}
public static void main(String[] args) {
    T o = new T();
    synchronized (o){
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
}

到這里可能有些小伙伴有疑問(wèn),這里為啥是輕量級(jí)鎖,不應(yīng)該先是偏向鎖嗎?原因如下:

因?yàn)槠蜴i是有4秒的延遲的,所以如果想要看到效果可以在代碼里加上sleep(4100)就可以了?;蛘呤峭ㄟ^(guò)jvm參數(shù)-XX:BiasedLockingStartupDelay=0將延遲設(shè)置成0

看完這里也對(duì)markword有了些了解了,因?yàn)樵趕ynchronized中加鎖就是通過(guò)cas的方式修改的markword中的鎖狀態(tài)

Synchronized的鎖升級(jí)

上圖大概就是Synchronized加鎖后的一個(gè)鎖升級(jí)的過(guò)程。從早期的重量級(jí)鎖優(yōu)化到了現(xiàn)在一個(gè)輕量級(jí)鎖。

偏向鎖

上面的重量級(jí)鎖說(shuō)到重量級(jí)鎖想要申請(qǐng)一把鎖需要用戶態(tài)到內(nèi)核態(tài)的一個(gè)轉(zhuǎn)換,到了后期的JDK版本中,加鎖不用在去向OS去申請(qǐng)鎖了,只需要在用戶態(tài)就可以完成加鎖。

從名字上可以看出偏向鎖它就是偏向某一個(gè)線程,把這個(gè)鎖加到這個(gè)線程上,在加鎖的時(shí)候如果發(fā)現(xiàn)當(dāng)前鎖的競(jìng)爭(zhēng)線程只有一個(gè)線程的話,那么這個(gè)鎖直接偏向這個(gè)線程。直接上鎖,不存在競(jìng)爭(zhēng)。并在線程棧中創(chuàng)建一個(gè)LR(鎖記錄)并將markword拷貝到LR中,同時(shí)鎖中的markwrod中的指針也會(huì)指向當(dāng)前持有鎖線程的LR

這里的LR是有什么作用?

首先synchronized是一個(gè)可重入鎖,它即然是一個(gè)可重入鎖它就得有一個(gè)東西用來(lái)記錄重入的次數(shù)(加鎖幾次必須解鎖幾次)。在解鎖時(shí)LR在棧中彈出一個(gè)就表示解鎖一次。

當(dāng)有多個(gè)線程競(jìng)爭(zhēng)的時(shí)候會(huì)升級(jí)成輕量級(jí)鎖(自旋鎖)

通過(guò)下圖來(lái)看下偏向鎖是怎么一回事。

當(dāng)大呆需要上WC時(shí),只有它自已要上WC,此時(shí)并沒(méi)有其它的人需要上WC,那么這時(shí)這個(gè)WC可以直接給大呆使用,并且大呆把可以標(biāo)識(shí)自已身份的ID貼到門上,表示此時(shí)大呆占用了這個(gè)WC。

當(dāng)又有一個(gè)線程來(lái)?yè)屨兼i時(shí)發(fā)現(xiàn)當(dāng)前鎖已被占用,此時(shí)鎖會(huì)從偏向鎖升級(jí)成輕量級(jí)鎖。

匿名偏向

在執(zhí)行的時(shí)候?qū)⑵蜴i的延遲設(shè)置成0-XX:BiasedLockingStartupDelay=0

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

可以看這個(gè)程序的執(zhí)行結(jié)果,當(dāng)前的鎖狀態(tài)是偏向鎖,而有意思是的鎖存在,但是他并沒(méi)有指向線程的指針,

這種情況稱為匿名偏向。

輕量級(jí)鎖

說(shuō)到輕量級(jí)鎖可能需要在兩種情況下來(lái)說(shuō)它,一是在升級(jí)成輕量鎖之前有偏向鎖,另一種是在升級(jí)輕量鎖之前沒(méi)有偏向鎖,這里說(shuō)完第一種第二種不用解釋各位也會(huì)明白是怎么一回事。

還是用上面這個(gè)圖來(lái)解釋,此時(shí)當(dāng)前的WC被大呆所占用,這時(shí)二呆來(lái)了也要使用WC。這時(shí)大呆和二呆就要通過(guò)CAS的方式來(lái)?yè)屨糤C。

因?yàn)榇藭r(shí)鎖的狀態(tài)是偏向鎖的狀態(tài),二呆來(lái)了也要使用WC(這時(shí)有兩個(gè)人同時(shí)要使用WC,這時(shí)就要將偏向鎖升級(jí)成輕量級(jí)鎖),在升級(jí)輕量鎖之前首先需要將WC上的標(biāo)識(shí)大呆身份的ID撕下來(lái)(這一步叫做偏向鎖的撤銷),然后能過(guò)自旋+CAS的方式兩個(gè)人來(lái)?yè)屾i。當(dāng)其中一個(gè)線程搶鎖成功后,會(huì)將LR貼到WC的門上,表示W(wǎng)C當(dāng)前被某個(gè)線程占用,然后另一個(gè)沒(méi)有搶到鎖的線程就一直自旋,當(dāng)自旋一定次數(shù)后升級(jí)成重量級(jí)鎖。

如果在升級(jí)輕量鎖之前沒(méi)有偏向鎖,此時(shí)兩個(gè)線程直接自旋+CAS的方式來(lái)?yè)屾i。

重量級(jí)鎖

在了解重量級(jí)鎖之前,我想應(yīng)該先說(shuō)下用戶態(tài)與內(nèi)核態(tài)

對(duì)于系統(tǒng)而言,它可以做的一些事情,普通的應(yīng)用程序是無(wú)法完成的,比如系統(tǒng)可以干掉硬盤,如果普通的程序想要干掉硬盤它必須向操作系統(tǒng)去申請(qǐng),由此操作系統(tǒng)中的指令分了級(jí)別,操作系統(tǒng)級(jí)別可以訪問(wèn)所有的指令,在用戶態(tài)下只能訪問(wèn)用戶能訪問(wèn)的指令,如果用戶態(tài)要訪問(wèn)內(nèi)核態(tài)可以執(zhí)行的指令必須去向操作系統(tǒng)去申請(qǐng),請(qǐng)操作系統(tǒng)調(diào)用。

在JDK早期,上鎖只能上重量級(jí)鎖。因?yàn)?,所謂的JVM其實(shí)它也是工作在用戶態(tài)的一個(gè)進(jìn)程,如果想要對(duì)一個(gè)對(duì)象進(jìn)行上鎖,那它必須去向系統(tǒng)去申請(qǐng)鎖。申請(qǐng)鎖成功后,還需要將這把鎖從內(nèi)核態(tài)返回到用戶態(tài),它稱為重量級(jí)鎖的原因就是在鎖申請(qǐng)的時(shí)候都要有一個(gè)在用戶態(tài)到內(nèi)核態(tài)的轉(zhuǎn)換

當(dāng)搶占到鎖后,markword里面記錄的不再是LR的指針,而是指向的是一個(gè)C++的對(duì)象ObjectMonitor,

如果當(dāng)前線程自旋一段時(shí)間后沒(méi)有搶到鎖就會(huì)升級(jí)成重量級(jí)鎖,并將當(dāng)前的線程存入EntryList隊(duì)列中阻塞,持有鎖的線程執(zhí)行完成后,在喚醒EntryList隊(duì)列中的線程去搶占鎖。

總結(jié)

synchronized的鎖升級(jí)過(guò)程:

  • 偏向鎖未啟動(dòng),創(chuàng)建出來(lái)的是普通對(duì)象, 如果有一個(gè)線程來(lái)?yè)屨兼i,該鎖偏向此線程,這時(shí)升級(jí)為偏向鎖。
    • 在偏向鎖的基礎(chǔ)上又來(lái)一個(gè)線程搶占鎖此時(shí)升級(jí)為輕量級(jí)鎖。當(dāng)一個(gè)線程沒(méi)有搶占到鎖,并且自旋了一定時(shí)間后還沒(méi)有搶到鎖,就會(huì)升級(jí)成重量級(jí)鎖。
  • 在偏向鎖的基礎(chǔ)上如果出現(xiàn)了重度競(jìng)爭(zhēng)就會(huì)直接升級(jí)成重量級(jí)鎖
  • 偏向鎖已啟動(dòng),創(chuàng)建出來(lái)的對(duì)象匿名偏向,后面的鎖升級(jí)和上面寫的一樣。

到此這篇關(guān)于Java中synchronized鎖升級(jí)的過(guò)程的文章就介紹到這了,更多相關(guān)synchronized鎖升級(jí)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot整合Mybatis實(shí)現(xiàn)商品評(píng)分的項(xiàng)目實(shí)踐

    SpringBoot整合Mybatis實(shí)現(xiàn)商品評(píng)分的項(xiàng)目實(shí)踐

    本文介紹了SpringBoot整合Mybatis-plus框架實(shí)現(xiàn)對(duì)商品評(píng)分的功能實(shí)現(xiàn)流程和前端接口實(shí)現(xiàn)過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-02-02
  • Java正則表達(dá)式循環(huán)匹配字符串方式

    Java正則表達(dá)式循環(huán)匹配字符串方式

    這篇文章主要介紹了Java正則表達(dá)式循環(huán)匹配字符串方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java 封裝基礎(chǔ)知識(shí)

    Java 封裝基礎(chǔ)知識(shí)

    這篇文章主要介紹了Java 封裝的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • java 使用POI合并兩個(gè)word文檔

    java 使用POI合并兩個(gè)word文檔

    這篇文章主要介紹了java 使用POI合并兩個(gè)word文檔的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Springboot發(fā)送郵件功能的實(shí)現(xiàn)詳解

    Springboot發(fā)送郵件功能的實(shí)現(xiàn)詳解

    電子郵件是—種用電子手段提供信息交換的通信方式,是互聯(lián)網(wǎng)應(yīng)用最廣的服務(wù)。本文詳細(xì)為大家介紹了SpringBoot實(shí)現(xiàn)發(fā)送電子郵件功能的示例代碼,需要的可以參考一下
    2022-09-09
  • Java多態(tài)用法與注意點(diǎn)實(shí)例分析

    Java多態(tài)用法與注意點(diǎn)實(shí)例分析

    這篇文章主要介紹了Java多態(tài)用法與注意點(diǎn),結(jié)合實(shí)例形式分析了java多態(tài)相關(guān)的向上轉(zhuǎn)型、向下轉(zhuǎn)型、隱藏等相關(guān)操作技巧,需要的朋友可以參考下
    2019-08-08
  • Java實(shí)現(xiàn)螺旋矩陣的示例

    Java實(shí)現(xiàn)螺旋矩陣的示例

    這篇文章主要介紹了Java實(shí)現(xiàn)螺旋矩陣的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • 詳解Java中的悲觀鎖與樂(lè)觀鎖

    詳解Java中的悲觀鎖與樂(lè)觀鎖

    樂(lè)觀鎖對(duì)應(yīng)于生活中樂(lè)觀的人總是想著事情往好的方向發(fā)展,悲觀鎖對(duì)應(yīng)于生活中悲觀的人總是想著事情往壞的方向發(fā)展.這兩種人各有優(yōu)缺點(diǎn),不能不以場(chǎng)景而定說(shuō)一種人好于另外一種人,文中詳細(xì)介紹了悲觀鎖與樂(lè)觀鎖,需要的朋友可以參考下
    2021-05-05
  • mybatis的insert插入后獲取自增id的方法詳解(從controller到mapper)

    mybatis的insert插入后獲取自增id的方法詳解(從controller到mapper)

    這篇文章主要介紹了mybatis的insert插入后獲取自增id的示例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-10-10
  • 計(jì)算一個(gè)Java對(duì)象占用字節(jié)數(shù)的方法

    計(jì)算一個(gè)Java對(duì)象占用字節(jié)數(shù)的方法

    這篇文章主要介紹了計(jì)算一個(gè)Java對(duì)象占用字節(jié)數(shù)的方法,較為詳細(xì)的分析了Java中各類對(duì)象所占用的字節(jié)數(shù),需要的朋友可以參考下
    2015-01-01

最新評(píng)論