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

淺析Java關(guān)鍵詞synchronized的使用

 更新時間:2022年12月30日 08:22:22   作者:拿了桔子跑-范德依彪  
Synchronized是java虛擬機為線程安全而引入的。這篇文章主要為大家介紹一下Java關(guān)鍵詞synchronized的使用與原理,需要的可以參考一下

1 引入Synchronized

  • Synchronized是java虛擬機為線程安全而引入的。
  • 互斥同步是一種最常見的并發(fā)正確性的保障手段。同步是指在多個線程并發(fā)訪問共享數(shù)據(jù)時,保證共享數(shù)據(jù)在同一個時刻只被一條線程使用。
  • synchronized是最基本的互斥同步手段,它是一種塊結(jié)構(gòu)的同步語法。
  • synchronized修飾代碼塊,無論該代碼塊正常執(zhí)行完成還是發(fā)生異常,都會釋放鎖

synchronized對線程訪問的影響:

  • 被synchronized修飾的同步塊在持有鎖的線程執(zhí)行完畢并釋放鎖之前,會阻塞其他線程的進入。
  • 被synchronized修飾的同步塊對同一條線程是可重入

2 Synchronized的使用

可以作用在方法上或者方法里的代碼塊:

  • 修飾方法,包括實例方法和靜態(tài)方法
  • 修飾方法里的代碼塊,這時需要一個引用作為參數(shù)。
  • Synchronized作用地方不同,產(chǎn)生的鎖類型也不同,分為對象鎖和類鎖

2.1 對象鎖

Synchronized修飾實例方法或者代碼塊(鎖對象不是*.class),此時生產(chǎn)對象鎖。多線程訪問該類的同一個對象的sychronized塊是同步的,訪問不同對象不受同步限制。

2.1.1 Synchronized修飾實例方法

public static void main(String[] args){
        TempTest tempTest = new TempTest();
        Thread t1 = new Thread(() -> {
            tempTest.doing(Thread.currentThread().getName());
        });
        Thread t2 = new Thread(() -> {
            tempTest.doing(Thread.currentThread().getName());
        });
        t1.start();
        t2.start();
    }

    //同一時刻只能被一個線程調(diào)用
    private synchronized void doing(String threadName){
        for(int i=0;i<3;i++){
            System.out.println("current thread is : "+threadName);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {}
        }
    }

運行結(jié)果:

current thread is : Thread-0
current thread is : Thread-0
current thread is : Thread-0
current thread is : Thread-1
current thread is : Thread-1
current thread is : Thread-1

2.1.2 Synchronized修飾代碼塊

public class SynchronizedObjectLock implements Runnable {
    static SynchronizedObjectLock instence = new SynchronizedObjectLock();
    @Override
    public void run() {
        // 同步代碼塊形式:鎖為this,兩個線程使用的鎖是一樣的,線程1必須要等到線程0釋放了該鎖后,才能執(zhí)行
        synchronized (this) {
            System.out.println("我是線程" + Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "結(jié)束");
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instence);
        Thread t2 = new Thread(instence);
        t1.start();
        t2.start();
    }
}

運行結(jié)果:

我是線程Thread-0
Thread-0結(jié)束
我是線程Thread-1
Thread-1結(jié)束

2.2 類鎖

synchronize修飾靜態(tài)方法或指定鎖對象為Class,此時產(chǎn)生類鎖。多線程訪問該類的所有對象的sychronized塊是同步的

2.2.1 synchronize修飾靜態(tài)方法

    public static void main(String[] args){
        TempTest tempTest1 = new TempTest();
        TempTest tempTest2 = new TempTest();
        //雖然創(chuàng)建了兩個TempTest實例,但是依然是調(diào)用同一個doing方法(因為是個static);因此doing還是會依次執(zhí)行
        Thread t1 = new Thread(() -> tempTest1.doing(Thread.currentThread().getName()));
        Thread t2 = new Thread(() -> tempTest2.doing(Thread.currentThread().getName()));
        t1.start();
        t2.start();
    }

    //修飾靜態(tài)方法,則是類鎖;
    private static synchronized void doing(String threadName){
        for(int i=0;i<3;i++){
            System.out.println("current thread is : "+threadName);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {}
        }
    }

運行結(jié)果:有序輸出 【如果去掉static ,則線程會交替執(zhí)行doing】

current thread is : Thread-0
current thread is : Thread-0
current thread is : Thread-0
current thread is : Thread-1
current thread is : Thread-1
current thread is : Thread-1

2.2.2 synchronize指定鎖對象為Class

public class SynchronizedObjectLock implements Runnable {
    static SynchronizedObjectLock instence1 = new SynchronizedObjectLock();
    static SynchronizedObjectLock instence2 = new SynchronizedObjectLock();

    @Override
    public void run() {
        // 所有線程需要的鎖都是同一把
        synchronized(SynchronizedObjectLock.class){
            System.out.println("我是線程" + Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "結(jié)束");
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instence1);
        Thread t2 = new Thread(instence2);
        t1.start();
        t2.start();
    }
}

結(jié)果:

我是線程Thread-0
Thread-0結(jié)束
我是線程Thread-1
Thread-1結(jié)束

3 Synchronized原理分析

3.1 虛擬機如何辨別和處理synchronized

虛擬機可以從常量池中的方法表結(jié)構(gòu)中的ACC_ SYNCHRONIZED訪問標(biāo)志區(qū)分一個方法是否是同步方法。

當(dāng)調(diào)用方法時,調(diào)用指令將會檢查方法的ACC_ SYNCHRONIZED訪問標(biāo)志是否設(shè)置,如果設(shè)置了,執(zhí)行線程將先持有同步鎖,然后執(zhí)行方法,最后在方法完成時釋放同步鎖。

在方法執(zhí)行期間,執(zhí)行線程持有了同步鎖,其他任何線程都無法再獲得同一個鎖。

如果一個同步方法執(zhí)行期間拋出了異常,并且在方法內(nèi)部無法處理此異常,那這個同步方法所持有的鎖將在異常拋到同步方法之外時自動釋放。

3.2 虛擬機對synchronized的編譯處理

以下代碼:

public class Foo {
    void onlyMe(Foo f) {
        synchronized(f) {
            doSomething();
        }
    }
    private void doSomething(){ }
}

編譯后,這段代碼生成的字節(jié)碼序列如下:

  • synchronized關(guān)鍵字經(jīng)過Javac編譯之后,會在同步塊的前后生成monitorentermonitorexit兩個字節(jié)碼指令。
  • 指令含義:monitorenter:獲取對象的鎖;monitorexit:釋放對象的鎖
  • 執(zhí)行monitorenter指令時,首先嘗試獲取對象的鎖。如果對象沒被鎖定,或者當(dāng)前線程已經(jīng)持有了對象的鎖,就把鎖的計數(shù)器的值增加1
  • 執(zhí)行monitorexit指令時,將鎖計數(shù)器的值減1,一旦計數(shù)器的值為零,鎖隨即就被釋放
  • 如果獲取對象鎖失敗,那當(dāng)前線程阻塞等待,直到鎖被釋放。
  • 為了保證在方法異常完成時monitorenter和monitorexit指令依然可以正確配對執(zhí)行,編譯器會自動產(chǎn)生一個異常處理程序,它的目的就是用來執(zhí)行monitorexit指令。

3.3 虛擬機執(zhí)行加鎖和釋放鎖的過程

那么重點來了到這里,有幾個問題需明確:

  • 什么叫對象的鎖?
  • 如何確定鎖被線程持有
  • 執(zhí)行monitorenter后,對象發(fā)生什么變化
  • 鎖計數(shù)值保存在哪里,如何獲取到?

1. 什么叫對象的鎖?對象的內(nèi)存結(jié)構(gòu)參考文末補充內(nèi)容

  • 鎖,一種可以被讀寫的資源,對象的鎖是對象的一部分。
  • 對象的結(jié)構(gòu)中有部分稱為對象頭。
  • 對象頭中有2bit空間,用于存儲鎖標(biāo)志,通過該標(biāo)志位來標(biāo)識對象是否被鎖定。

2. 如果確定鎖被線程持有?

  • 代碼即將進入同步塊的時,如果鎖標(biāo)志位為“01”(對象未被鎖定),虛擬機首先將在當(dāng)前線程的棧幀中建立一個名為鎖記錄的空間,存儲鎖對象Mark Word的拷貝。(線程開辟空間并存儲對象頭)
  • 虛擬機將使用CAS操作嘗試把對象的Mark Word更新成指向鎖記錄的指針(對象頭的mw存儲指向線程“鎖記錄”中的指針)
  • 如果CAS操作成功,即代表該線程擁有了這個對象的鎖,并且將對象的鎖標(biāo)志位轉(zhuǎn)變?yōu)?ldquo;00”
  • 如果CAS操作失敗,那就意味著至少存在一條線程與當(dāng)前線程競爭獲取該對象的鎖。虛擬機首先會檢查對象的Mark Word是否指向當(dāng)前線程的棧幀,如果是,說明當(dāng)前線程已經(jīng)擁有了這個對象的鎖,那直接進入同步塊繼續(xù)執(zhí)行,否則就說明這個鎖對象已經(jīng)被其他線程搶占。
  • 解鎖過程:CAS操作把線程中保存的MW拷貝替換回對象頭中。假如能夠成功替換,那整個同步過程就順利完成了;如果替換失敗,則說明有其他線程嘗試過獲取該鎖,就要在釋放鎖的同時,喚醒被掛起的線程。

3 執(zhí)行monitorenter后,對象發(fā)生什么變化?

  • 對象的鎖標(biāo)志位轉(zhuǎn)變?yōu)?ldquo;00”
  • 擁有對象鎖的線程開辟了新空間,保存了對象的Mark Word信息
  • 對象的Mark Word保存了線程的鎖記錄空間的地址拷貝

4 鎖計數(shù)值保存在哪里?

我還沒搞懂。

monitorenter指令執(zhí)行的過程

4 Synchronized與Lock

synchronized的缺陷

  • 在多線程競爭鎖時,當(dāng)一個線程獲取鎖時,它會阻塞所有正在競爭的線程,這樣對性能帶來了極大的影響。
  • 掛起線程和恢復(fù)線程的操作都需要轉(zhuǎn)入內(nèi)核態(tài)中完成,上下文切換需要消耗很大性能。
  • 效率低:鎖的釋放情況少,只有代碼執(zhí)行完畢或者異常結(jié)束才會釋放鎖;試圖獲取鎖的時候不能設(shè)定超時,不能中斷一個正在使用鎖的線程,相對而言,Lock可以中斷和設(shè)置超時
  • 不夠靈活:加鎖和釋放的時機單一,每個鎖僅有一個單一的條件(某個對象),相對而言,讀寫鎖更加靈活

5 使用Synchronized有哪些要注意的

鎖對象不能為空,因為鎖的信息都保存在對象頭里

作用域不宜過大,影響程序執(zhí)行的速度,控制范圍過大,編寫代碼也容易出錯

在能選擇的情況下,既不要用Lock也不要用synchronized關(guān)鍵字,用java.util.concurrent包中的各種各樣的類,如果有必要,使用synchronized關(guān)鍵,因為代碼量少,避免出錯

synchronized實際上是非公平的,新來的線程有可能立即獲得執(zhí)行,而在等待區(qū)中等候已久的線程可能再次等待,這樣有利于提高性能,但是也可能會導(dǎo)致饑餓現(xiàn)象。

知識補充

Java內(nèi)存層面的對象認(rèn)識 

說明:此分析基于HotSpot虛擬機

1 對象的創(chuàng)建

Java對象的創(chuàng)建方式有三種:

  • 通過new創(chuàng)建
  • 通過反序列化創(chuàng)建
  • 通過復(fù)制創(chuàng)建

通過new方式的對象創(chuàng)建過程如下:

創(chuàng)建過程說明:

  • 執(zhí)行字節(jié)碼遇到new指令時,首先將去檢查這個指令的參數(shù)是否能在常量池中定位到 一個類的符號引用。
  • 類的初始化過程在后續(xù)章節(jié)詳細(xì)補充
  • 給對象分配初始內(nèi)存空間有兩種方式:指針碰撞 和 空閑列表。
  • 分配空間后,清空該段的【不包括對象頭】值,保證對象屬性的不設(shè)值就使用初始值
  • 對象頭信息包括:元數(shù)據(jù)信息、對象的哈希碼、對象的GC分代年齡
  • 執(zhí)行構(gòu)造函數(shù),給屬性初始化設(shè)置的值

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

對象存儲的內(nèi)容分類以及明細(xì)如下:

關(guān)于對象頭的補充說明

  • 對象頭,在字長為32位和64位的虛擬機中分別為32比特(4字節(jié))64比特(8字節(jié))。
  • 對象頭的類型指針:不一定所有對象都會存儲這項信息,意味著訪問對象所屬的類不一定通過對象自身。
  • 如果對象是一個Java數(shù)組,那在對象頭中還必須有一塊用于記錄數(shù)組長度的數(shù)據(jù)。

關(guān)于實例數(shù)據(jù)的補充說明

  • 對象屬性的存儲順序,受到虛擬機分配策略參數(shù)(-XX:FieldsAllocationStyle參數(shù))和字段在Java源碼中定義順序的影響
  • 默認(rèn)的分配策略下:占據(jù)相同字節(jié)數(shù)的屬性會排列在一起,滿足該條件下,父類的屬性排在前面。

關(guān)于對齊填充的說明

不一定會存在,因為對象的大小一定是8字節(jié)的整數(shù)倍,因此需要對齊填充這部分,充當(dāng)占位符

在32位字長的虛擬機下,對象的內(nèi)存分布情況如下:

3 對象的訪問定位

對象訪問方式也是由虛擬機實現(xiàn)而定的,主流的訪問方式主要有使用句柄和直接指針兩種:

3.1句柄訪問

說明:

句柄訪問方式,Java堆中將可能會劃分出一塊內(nèi)存來作為句柄池,reference中存儲的就是對象的句柄地址,而句柄中包含了對象實例數(shù)據(jù)與類型數(shù)據(jù)各自具體的地址

3.2 直接指針訪問

說明:

使用直接指針來訪問最大的好處就是速度更快,只需要一次定位就能找到實例數(shù)據(jù),而句柄池則需要兩次:(需要先定位句柄池,再定位實例數(shù)據(jù))

以上就是淺析Java關(guān)鍵詞synchronized的使用的詳細(xì)內(nèi)容,更多關(guān)于Java關(guān)鍵詞synchronized的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 解讀java?try?catch?異常后還會繼續(xù)執(zhí)行嗎

    解讀java?try?catch?異常后還會繼續(xù)執(zhí)行嗎

    這篇文章主要介紹了解讀java?try?catch?異常后還會不會繼續(xù)執(zhí)行問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • 值得收藏的SpringBoot 實用的小技巧

    值得收藏的SpringBoot 實用的小技巧

    最近分享的一些源碼、框架設(shè)計的東西。我發(fā)現(xiàn)大家熱情不是特別高,想想大多數(shù)應(yīng)該還是正兒八經(jīng)寫代碼的居多;這次就分享一點接地氣的: SpringBoot 使用中的一些小技巧 ,需要的朋友可以參考下
    2018-10-10
  • Java設(shè)計模式之創(chuàng)建者模式詳解

    Java設(shè)計模式之創(chuàng)建者模式詳解

    這篇文章主要介紹了Java設(shè)計模式之創(chuàng)建者模式詳解,創(chuàng)建者模式,顧名思義,就是提供友好的創(chuàng)建對象的方式?,對象都是?new?出來的,但是在一些情況下,這種方式不是很友好,首先,它不夠直觀,需要的朋友可以參考下
    2023-08-08
  • SpringCloud Feign遠(yuǎn)程調(diào)用實現(xiàn)詳解

    SpringCloud Feign遠(yuǎn)程調(diào)用實現(xiàn)詳解

    Feign是Netflix公司開發(fā)的一個聲明式的REST調(diào)用客戶端; Ribbon負(fù)載均衡、 Hystrⅸ服務(wù)熔斷是我們Spring Cloud中進行微服務(wù)開發(fā)非?;A(chǔ)的組件,在使用的過程中我們也發(fā)現(xiàn)它們一般都是同時出現(xiàn)的,而且配置也都非常相似
    2022-11-11
  • 通過Java實現(xiàn)添加或刪除PDF中的附件

    通過Java實現(xiàn)添加或刪除PDF中的附件

    當(dāng)我們在制作PDF文件或者PPT演示文稿的時候,為了讓自己的文件更全面詳細(xì),就會在文件中添加附件。本文為大家整理了Java實現(xiàn)添加或刪除PDF中的附件的方法,需要的可以參考下
    2023-01-01
  • Java volatile關(guān)鍵字特性講解上篇

    Java volatile關(guān)鍵字特性講解上篇

    JMM要求保證可見性、原子性、有序性,volatile可以保證其中的兩個,本篇文章具體驗證volatile的可見性,不原子性和禁重排,同時解決volatile的不保證原子性,讓代碼具有原子性
    2022-12-12
  • SpringBoot使用攔截器Interceptor實現(xiàn)統(tǒng)一角色權(quán)限校驗

    SpringBoot使用攔截器Interceptor實現(xiàn)統(tǒng)一角色權(quán)限校驗

    角色權(quán)限校驗,是保證接口安全必備的能力:有權(quán)限才可以操作,所以,一般對于這種通用邏輯,推薦不與主業(yè)務(wù)邏輯耦合,那么怎么來解耦,那么本文小編就給大家詳細(xì)講解如何使用攔截器Interceptor實現(xiàn)統(tǒng)一角色權(quán)限校驗,需要的朋友可以參考下
    2023-07-07
  • java開發(fā)微信分享到朋友圈功能

    java開發(fā)微信分享到朋友圈功能

    這篇文章主要為大家詳細(xì)介紹了java開發(fā)微信發(fā)送給朋友和分享到朋友圈功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-07-07
  • Java8 Comparator排序方法實例詳解

    Java8 Comparator排序方法實例詳解

    這篇文章主要介紹了Java8 Comparator排序方法實例詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12
  • mybatis in查詢條件過長的解決方案

    mybatis in查詢條件過長的解決方案

    這篇文章主要介紹了mybatis in查詢條件過長的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10

最新評論

武城县| 瓦房店市| 宣汉县| 乐业县| 疏勒县| 扎赉特旗| 金坛市| 和龙市| 定州市| 沾益县| 都匀市| 安图县| 若尔盖县| 嵊州市| 秦皇岛市| 眉山市| 景宁| 博白县| 怀安县| 讷河市| 深圳市| 凌海市| 林甸县| 大足县| 马尔康县| 昆明市| 和龙市| 云和县| 湘阴县| 菏泽市| 肇庆市| 大关县| 宜良县| 蓝山县| 大方县| 韶关市| 冀州市| 永胜县| 金山区| 旬阳县| 西贡区|