最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java?synchronized同步關(guān)鍵字工作原理

 更新時(shí)間:2023年02月13日 09:08:34   作者:每天都要進(jìn)步一點(diǎn)點(diǎn)  
synchronized作為Java程序員最常用同步工具,很多人卻對(duì)它的用法和實(shí)現(xiàn)原理一知半解,以至于還有不少人認(rèn)為synchronized是重量級(jí)鎖,性能較差,盡量少用。但不可否認(rèn)的是synchronized依然是并發(fā)首選工具,本文就來詳細(xì)講講

一、簡(jiǎn)介

synchronized是一個(gè)同步關(guān)鍵字,在某些多線程場(chǎng)景下,如果不進(jìn)行同步會(huì)導(dǎo)致共享數(shù)據(jù)不安全,synchronized關(guān)鍵字就可以用于代碼同步。

synchronized主要有3種使用形式:

  • 修飾普通同步方法

鎖的對(duì)象是當(dāng)前實(shí)例對(duì)象;

  • 修飾靜態(tài)同步方法

鎖的對(duì)象是當(dāng)前的類的Class字節(jié)碼對(duì)象;

  • 修飾同步代碼塊

鎖的對(duì)象是synchronized后面括號(hào)里配置的對(duì)象,可以是某個(gè)對(duì)象,也可以是某個(gè)類的.class對(duì)象;

二、synchronized的特性

1)、原子性

原子性指的是在一次或多次操作中,要么所有的操作都執(zhí)行并且不會(huì)受其他因素干擾而中斷,要么所有的操作都不執(zhí)行。

2)、可見性

可見性是指一個(gè)線程對(duì)共享變量進(jìn)行了修改,另一個(gè)線程可以立即讀取得到修改后的最新值。

獲取鎖時(shí),會(huì)清空當(dāng)前線程工作內(nèi)存中共享變量的副本值,重新從主內(nèi)存中獲取變量最新的值;

釋放鎖時(shí),會(huì)將工作內(nèi)存的值重新刷新回主內(nèi)存;

3)、有序性

有序性是指程序中代碼的執(zhí)行順序,Java在編譯時(shí)和運(yùn)行時(shí)會(huì)對(duì)代碼進(jìn)行優(yōu)化,會(huì)導(dǎo)致程序最終的執(zhí)行順序不一定就是我們編寫代碼時(shí)的順序。

例如,instance = new Singleton()實(shí)例化對(duì)象的語句分為三步:

  • 1、分配對(duì)象的內(nèi)存空間;
  • 2、初始化對(duì)象;
  • 3、設(shè)置實(shí)例對(duì)象指向剛分配的內(nèi)存地址;

上述第二步操作需要依賴第一步,但是第三步操作不需要依賴第二步,所以執(zhí)行順序可能為:1->2->3、1->3->2,當(dāng)執(zhí)行順序?yàn)?->3->2時(shí),可能實(shí)例對(duì)象還沒正確初始化,我們直接拿到使用的時(shí)候可能會(huì)報(bào)錯(cuò)。

synchronized的有序性是依靠?jī)?nèi)存屏障實(shí)現(xiàn)的,在 monitorenter 指令和 Load 屏障之后,會(huì)加一個(gè) Acquire屏障,這個(gè)屏障的作用是禁止同步代碼塊里面的讀操作和外面的讀寫操作之間發(fā)生指令重排,在 monitorexit 指令前加一個(gè)Release屏障,也是禁止同步代碼塊里面的寫操作和外面的讀寫操作之間發(fā)生重排序。如下:

int a = 0;
synchronize (this){  //monitorenter
    // Load內(nèi)存屏障
    // Acquire屏障,禁止代碼塊內(nèi)部的讀,和外面的讀寫發(fā)生指令重排
    int b = a;
    a = 10;    //注意:內(nèi)部還是會(huì)發(fā)生指令重排
    // Release屏障,禁止寫,和外面的讀寫發(fā)生指令重排
} //monitorexit
//Store內(nèi)存屏障

4)、可重入特性

可重入指的就是一個(gè)線程可以多次執(zhí)行synchronized,重復(fù)獲取同一把鎖。

舉個(gè)例子:

public class RenentrantDemo {
    // 鎖對(duì)象
    private static Object obj = new Object();
    public static void main(String[] args) {
        // 自定義Runnable對(duì)象
        Runnable runnable = () -> {
            //  使用嵌套的同步代碼塊
            synchronized (obj) {
                System.out.println(Thread.currentThread().getName() + "第一次獲取鎖資源...");
                synchronized (obj) {
                    System.out.println(Thread.currentThread().getName() + "第二次獲取鎖資源...");
                    synchronized (obj) {
                        System.out.println(Thread.currentThread().getName() + "第三次獲取鎖資源...");
                    }
                }
            }
        };
        new Thread(runnable, "t1").start();
    }
}

運(yùn)行結(jié)果:

t1第一次獲取鎖資源...
t1第二次獲取鎖資源...
t1第三次獲取鎖資源...

三、synchonized的使用及通過反匯編分析其原理

修飾代碼塊

public class SynchronizedDemo01 {
    // 鎖對(duì)象
    private static Object obj = new Object();
    public static void main(String[] args) {
        synchronized (obj) {
            System.out.println("execute main()...");
        }
    }
}

使用javap -p -v .\SynchronizedDemo01.class命令對(duì)字節(jié)碼進(jìn)行反匯編,查看字節(jié)碼指令:

monitorenter指令

官網(wǎng)對(duì)monitorenter指令的介紹,就是說每一個(gè)對(duì)象都會(huì)和一個(gè)監(jiān)視器對(duì)象monitor關(guān)聯(lián),監(jiān)視器被占用時(shí)會(huì)被鎖住,其他線程無法來獲取該monitor。 當(dāng)JVM執(zhí)行某個(gè)線程的某個(gè)方法內(nèi)部的monitorenter時(shí),它會(huì)嘗試去獲取當(dāng)前對(duì)象對(duì)應(yīng)的monitor的所有權(quán)。大體過程如下:

1. 若monior的進(jìn)入數(shù)為0,線程可以進(jìn)入monitor,并將monitor的進(jìn)入數(shù)置為1,當(dāng)前線程成為monitor的owner(擁有這把鎖的線程);

2. 若線程已擁有monitor的所有權(quán),允許它重入monitor,則進(jìn)入monitor的進(jìn)入數(shù)加1(記錄線程擁有鎖的次數(shù));

3. 若其他線程已經(jīng)占有monitor的所有權(quán),那么當(dāng)前嘗試獲取monitor的所有權(quán)的線程會(huì)被阻塞,直到monitor的進(jìn)入數(shù)變?yōu)?,才能重新嘗試獲取monitor的所有權(quán);

monitorexit指令

官網(wǎng)對(duì)monitorexit指令的介紹,就是說能執(zhí)行monitorexit指令的線程一定是擁有當(dāng)前對(duì)象的monitor的所有權(quán)的線程;執(zhí)行monitorexit時(shí)會(huì)將monitor的進(jìn)入數(shù)減1,當(dāng)monitor的進(jìn)入數(shù)減為0時(shí),當(dāng)前線程退出。

為什么字節(jié)碼中存在兩個(gè)monitorexit指令?

其實(shí)第二個(gè)monitorexit指令,是在程序發(fā)生異常時(shí)候用到的,也就說明了synchronized在發(fā)生異常時(shí),會(huì)自動(dòng)釋放鎖。

ObjectMonitor對(duì)象監(jiān)視器結(jié)構(gòu)如下:

ObjectMonitor() {
    _header       = NULL;		//鎖對(duì)象的原始對(duì)象頭
    _count        = 0;			//搶占當(dāng)前鎖的線程數(shù)量
    _waiters      = 0,			//調(diào)用wait方法后等待的線程數(shù)量
    _recursions   = 0;			//記錄鎖重入次數(shù)
    _object       = NULL;
    _owner        = NULL;		//指向持有ObjectMonitor的線程
    _WaitSet      = NULL;		//處于wait狀態(tài)的線程隊(duì)列,等待被喚醒
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ;		//等待鎖的線程隊(duì)列
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;
  }

修飾普通方法

public class SynchronizedDemo02 {
    public static void main(String[] args) {
    }
    // 修飾普通方法
    public synchronized void add() {
        System.out.println("add...");
    }
}

使用javap -p -v .\SynchronizedDemo02.class命令對(duì)字節(jié)碼進(jìn)行反匯編,查看字節(jié)碼指令:

如上圖,我們可以看到同步方法在反匯編后,不再是通過插入monitorentry和monitorexit指令實(shí)現(xiàn),而是會(huì)增加 ACC_SYNCHRONIZED 標(biāo)識(shí)隱式實(shí)現(xiàn)的,如果方法表結(jié)構(gòu)(method_info Structure)中的ACC_SYNCHRONIZED標(biāo)志被設(shè)置,那么線程在執(zhí)行方法前會(huì)先去獲取對(duì)象的monitor對(duì)象,如果獲取成功則執(zhí)行方法代碼,執(zhí)行完畢后釋放monitor對(duì)象,如果monitor對(duì)象已經(jīng)被其它線程獲取,那么當(dāng)前線程被阻塞。

修飾靜態(tài)方法

public class SynchronizedDemo03 {
    public static void main(String[] args) {
        add();
    }
    // 修飾靜態(tài)方法
    public synchronized static void add() {
        System.out.println("add...");
    }
}

使用javap -p -v .\SynchronizedDemo03.class命令對(duì)字節(jié)碼進(jìn)行反匯編,查看字節(jié)碼指令:

四、synchronized鎖對(duì)象存在哪里

之前對(duì)對(duì)象的內(nèi)存布局的介紹中,我們知道一個(gè)對(duì)象,包括對(duì)象頭、實(shí)例數(shù)據(jù)、對(duì)齊填充。而對(duì)象頭又包括mark word標(biāo)記字、類型指針、數(shù)組長(zhǎng)度(只有數(shù)組對(duì)象才有)。在mark word標(biāo)記字中,有一塊區(qū)域主要存放關(guān)于鎖的信息。

存在鎖對(duì)象的對(duì)象頭的MarkWord標(biāo)記字中。如下圖:

五、synchronized與lock的區(qū)別

區(qū)別synchronizedlock
1關(guān)鍵字接口
2自動(dòng)釋放鎖必須手動(dòng)調(diào)用unlock()方法釋放鎖
3不能知道線程是否拿到鎖可以知道線程是否拿到鎖
4能鎖住方法和代碼塊只能鎖住代碼塊
5讀、寫操作都阻塞可以使用讀鎖,提高多線程讀效率;
6非公平鎖通過構(gòu)造方法可指定是公平鎖/非公平鎖

六、總結(jié)

1、synchronized修飾代碼塊的時(shí)候,通過在生成的字節(jié)碼指令中插入monitorenter和monitorexit指令來完成對(duì)對(duì)象監(jiān)視器鎖的獲取和釋放;

2、synchronized修飾普通方法和靜態(tài)方法的時(shí)候,通過在字節(jié)碼中的方法頭信息中添ACC_SYNCHRONIZED標(biāo)識(shí),線程在執(zhí)行方法前會(huì)先去獲取對(duì)象的monitor對(duì)象,如果獲取成功則執(zhí)行方法代碼,執(zhí)行完畢后釋放monitor對(duì)象;

3、synchronized修飾代碼塊,鎖的對(duì)象就是代碼塊中的對(duì)象;修飾普通方法的時(shí)候,鎖的對(duì)象就是當(dāng)前對(duì)象this;修飾靜態(tài)方法的時(shí)候,鎖的對(duì)象就是當(dāng)前類的Class字節(jié)碼對(duì)象(類對(duì)象);

4、使用synchronized修飾實(shí)例對(duì)象時(shí),如果一個(gè)線程正在訪問實(shí)例對(duì)象的一個(gè)synchronized方法時(shí),其它線程不僅不能訪問該synchronized方法,該對(duì)象的其它synchronized方法也不能訪問,因?yàn)橐粋€(gè)對(duì)象只有一個(gè)監(jiān)視器鎖對(duì)象,但是其它線程可以訪問該對(duì)象的非synchronized方法。

5、線程A訪問實(shí)例對(duì)象的非static synchronized方法時(shí),線程B也可以同時(shí)訪問實(shí)例對(duì)象的static synchronized方法,因?yàn)榍罢攉@取的是實(shí)例對(duì)象的監(jiān)視器鎖,而后者獲取的是類對(duì)象的監(jiān)視器鎖,兩者不存在互斥關(guān)系。

到此這篇關(guān)于Java synchronized同步關(guān)鍵字工作原理的文章就介紹到這了,更多相關(guān)Java synchronized內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java隨機(jī)數(shù)生產(chǎn)算法實(shí)例

    java隨機(jī)數(shù)生產(chǎn)算法實(shí)例

    下面小編就為大家?guī)硪黄猨ava隨機(jī)數(shù)生產(chǎn)算法實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • Log4j2?重大漏洞編譯好的log4j-2.15.0.jar包下載(替換過程)

    Log4j2?重大漏洞編譯好的log4j-2.15.0.jar包下載(替換過程)

    Apache?開源項(xiàng)目?Log4j?的遠(yuǎn)程代碼執(zhí)行漏洞細(xì)節(jié)被公開,由于?Log4j?的廣泛使用,該漏洞一旦被攻擊者利用會(huì)造成嚴(yán)重危害,下面小編給大家?guī)砹薒og4j2?重大漏洞編譯好的log4j-2.15.0.jar包下載,感興趣的朋友一起看看吧
    2021-12-12
  • Spring編程式和聲明式事務(wù)實(shí)例講解小結(jié)

    Spring編程式和聲明式事務(wù)實(shí)例講解小結(jié)

    這篇文章主要介紹了Spring編程式和聲明式事務(wù)實(shí)例講解小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • java 裝飾模式(Decorator Pattern)詳解及實(shí)例代碼

    java 裝飾模式(Decorator Pattern)詳解及實(shí)例代碼

    裝飾器模式(Decorator Pattern)允許向一個(gè)現(xiàn)有的對(duì)象添加新的功能,同時(shí)又不改變其結(jié)構(gòu)。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它是作為現(xiàn)有的類的一個(gè)包裝
    2016-10-10
  • Java中定時(shí)器Timer致命缺點(diǎn)案例詳解

    Java中定時(shí)器Timer致命缺點(diǎn)案例詳解

    這篇文章主要介紹了Java中定時(shí)器Timer致命缺點(diǎn),以Java中定時(shí)器Time為案例整理下我的學(xué)習(xí)方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-02-02
  • Java線程組與未處理異常實(shí)例分析

    Java線程組與未處理異常實(shí)例分析

    這篇文章主要介紹了Java線程組與未處理異常,結(jié)合實(shí)例形式分析了java線程組處理異常的相關(guān)技巧與操作注意事項(xiàng),需要的朋友可以參考下
    2019-09-09
  • 詳解關(guān)于java文件下載文件名亂碼問題解決方案

    詳解關(guān)于java文件下載文件名亂碼問題解決方案

    這篇文章主要介紹了詳解關(guān)于java文件下載文件名亂碼問題解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • Mybatis Plus Wrapper查詢某幾列的方法實(shí)現(xiàn)

    Mybatis Plus Wrapper查詢某幾列的方法實(shí)現(xiàn)

    MybatisPlus中,使用Wrapper的select和notSelect方法可以精確控制查詢的字段,本文就來介紹一下Mybatis Plus Wrapper查詢某幾列的方法實(shí)現(xiàn),感興趣的可以了解一下
    2024-10-10
  • Log4j關(guān)閉Spring和Hibernate日志打印方式

    Log4j關(guān)閉Spring和Hibernate日志打印方式

    這篇文章主要介紹了Log4j關(guān)閉Spring和Hibernate日志打印方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java8如何從一個(gè)Stream中過濾null值

    Java8如何從一個(gè)Stream中過濾null值

    這篇文章主要介紹了Java8如何從一個(gè)Stream中過濾null值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05

最新評(píng)論

茶陵县| 龙泉市| 依兰县| 七台河市| 全椒县| 太湖县| 华宁县| 鹤山市| 阿勒泰市| 翁源县| 临猗县| 开封县| 旅游| 新田县| 漠河县| 全南县| 南澳县| 德阳市| 北京市| 寿宁县| 公安县| 普定县| 清原| 海兴县| 保康县| 南岸区| 来宾市| 南郑县| 宣武区| 塔河县| 澜沧| 新巴尔虎左旗| 永城市| 金阳县| 于田县| 镇沅| 临西县| 正蓝旗| 汽车| 扶风县| 四川省|