精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

synchronized的原理與四種用法介紹

冬至子 ? 來源:并發(fā)編程之美 ? 作者:妙啊 ? 2023-06-09 16:13 ? 次閱讀

介紹

JDK提供的鎖分兩種,一種是JVM實現(xiàn)的synchronized,是java的關(guān)鍵字,因此在這個關(guān)鍵字作用對象的范圍內(nèi)都是可以保證原子性的,主要是依賴特殊的CPU指令。另一種是JDK提供的代碼層面的鎖Lock。

一、synchronized的四種用法

1. 修飾代碼塊

大括號括起來的代碼,稱同步語句塊,作用范圍是大括號,作用對象是調(diào)用代碼塊的對象。

public void test1(int j) {
    synchronized (this) {
        for (int i = 0; i < 10; i++) {
            log.info("test1 {} - {}", j, i);
        }
    }
}

測試代碼:

public static void main(String[] args) {
    SynchronizedExample1 example1 = new SynchronizedExample1();
    SynchronizedExample1 example2 = new SynchronizedExample1();
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(() - > {
        example1.test1(1);
    });
    executorService.execute(() - > {
        example2.test1(2);
    });
}

測試結(jié)果:

圖片

  • 可以看到test1方法的參數(shù)1和參數(shù)2是交替執(zhí)行。

2. 修飾方法

被修飾的方法稱為同步方法,作用范圍是整個方法,作用于調(diào)用對象。

public synchronized void test2(int j) {
    for (int i = 0; i < 10; i++) {
        log.info("test2 {} - {}", j, i);
    }
}

測試代碼:

public static void main(String[] args) {
    SynchronizedExample1 example1 = new SynchronizedExample1();
    SynchronizedExample1 example2 = new SynchronizedExample1();
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(() - > {
        example1.test2(1);
    });
    executorService.execute(() - > {
        example2.test2(2);
    });
}

測試結(jié)果:

圖片

  • 可以看到test2方法的參數(shù)1和參數(shù)2是交替執(zhí)行。

3. 修飾靜態(tài)方法

作用范圍是整個方法,作用于所有對象。

public static synchronized void test3(int j) {
    for (int i = 0; i < 10; i++) {
        log.info("test3 {} - {}", j, i);
    }
}

測試代碼:

public static void main(String[] args) {
    SynchronizedExample1 example1 = new SynchronizedExample1();
    SynchronizedExample1 example2 = new SynchronizedExample1();
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(() - > {
        example1.test3(1);
    });
    executorService.execute(() - > {
        example2.test3(2);
    });
}

測試結(jié)果:

圖片

  • 可以看到test3方法的參數(shù)1和參數(shù)2是1執(zhí)行完才執(zhí)行的2。

4. 修飾類

作用范圍是synchronized后面括號括起來的部分,作用于所有對象。

public static void test4(int j) {
    synchronized (SynchronizedExample2.class) {
        for (int i = 0; i < 10; i++) {
            log.info("test4 {} - {}", j, i);
        }
    }
}

測試代碼:

public static void main(String[] args) {
    SynchronizedExample1 example1 = new SynchronizedExample1();
    SynchronizedExample1 example2 = new SynchronizedExample1();
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(() - > {
        example1.test4(1);
    });
    executorService.execute(() - > {
        example2.test4(2);
    });
}

測試結(jié)果:

圖片

  • 可以看到test4方法的參數(shù)1和參數(shù)2是1執(zhí)行完才執(zhí)行的2。

二、synchronized的原理

在Java語言中存在兩種內(nèi)建的synchronized語法:synchronized語句、synchronized方法:

  • synchronized語句:當(dāng)源代碼被編譯成字節(jié)碼的時候,會在同步塊的入口位置和退出位置分別插入monitorenter和monitorexit字節(jié)碼指令。
  • synchronized方法:在Class文件的方法表中將該方法的access_flags字段中的synchronized標(biāo)志位置1

synchronized語句

如上示例,test1和test4使用的就是synchronized語句。使用Javap -c命令反編譯test1代碼,如下:

圖片

在Java虛擬機(jī)的specification中,有關(guān)于monitorenter和monitorexit字節(jié)碼指令的詳細(xì)描述:

monitorenter

每個對象都有一個鎖,也就是監(jiān)視器(monitor)。 Monitor可以理解為一個同步工具或一種同步機(jī)制,通常被描述為一個對象。 每一個Java對象就有一把看不見的鎖,稱為內(nèi)部鎖或者M(jìn)onitor鎖。

當(dāng)monitor被占有時就表示它被鎖定。線程執(zhí)行monitorenter指令時嘗試獲取對象所對應(yīng)的monitor的所有權(quán),過程如下:

  • 如果monitor的進(jìn)入數(shù)為0,則該線程進(jìn)入monitor,然后將進(jìn)入數(shù)設(shè)置為1,該線程即為monitor的所有者。
  • 如果線程已經(jīng)擁有了該monitor,只是重新進(jìn)入,則進(jìn)入monitor的進(jìn)入數(shù)加1。
  • 如果其他線程已經(jīng)占用了monitor,則該線程進(jìn)入阻塞狀態(tài),直到monitor的進(jìn)入數(shù)為0,再重新嘗試獲取monitor的所有權(quán)。

monitorexit

執(zhí)行monitorexit的線程必須是相應(yīng)的monitor的所有者。指令執(zhí)行時,monitor的進(jìn)入數(shù)減1,如果減1后進(jìn)入數(shù)為0,那線程退出monitor,不再是這個monitor的所有者。其他被這個monitor阻塞的線程可以嘗試去獲取這個monitor的所有權(quán)。

synchronized方法

如上示例,test2和test3使用的就是synchronized方法。synchronized方法加鎖的方式是在Class文件的方法表中將該方法的access_flags字段中的synchronized標(biāo)志位置1。如下:

圖片

  • 訪問標(biāo)志的第11位即為加鎖標(biāo)記位。

三、synchronized的優(yōu)化

synchronized在操作同步資源之前需要給同步資源先加鎖,這把鎖就是存在Java對象頭里,而Java對象頭又是什么呢?

Java對象頭

以Hotspot虛擬機(jī)為例,Hotspot的對象頭主要包括兩部分?jǐn)?shù)據(jù):Mark Word(標(biāo)記字段)、Klass Pointer(類型指針)。

Mark Word

默認(rèn)存儲對象的HashCode,分代年齡和鎖標(biāo)志位信息。這些信息都是與對象自身定義無關(guān)的數(shù)據(jù),所以Mark Word被設(shè)計成一個非固定的數(shù)據(jù)結(jié)構(gòu)以便在極小的空間內(nèi)存存儲盡量多的數(shù)據(jù)。它會根據(jù)對象的狀態(tài)復(fù)用自己的存儲空間,也就是說在運行期間Mark Word里存儲的數(shù)據(jù)會隨著鎖標(biāo)志位的變化而變化。

Klass Point

對象指向它的類元數(shù)據(jù)的指針,虛擬機(jī)通過這個指針來確定這個對象是哪個類的實例。

在JDK1.6及其之前的版本中monitorenter和monitorexit字節(jié)碼依賴于底層的操作系統(tǒng)的Mutex Lock來實現(xiàn)的,但是由于使用Mutex Lock需要將當(dāng)前線程掛起并從用戶態(tài)切換到內(nèi)核態(tài)來執(zhí)行,這種切換的代價是非常昂貴的。然而在現(xiàn)實中的大部分情況下,同步方法是運行在單線程環(huán)境(無鎖競爭環(huán)境)。如果每次都調(diào)用Mutex Lock將嚴(yán)重的影響程序的性能。因此在JDK6中為了減少獲得鎖和釋放鎖帶來的性能消耗,引入了偏向鎖輕量級鎖 。所以目前鎖一共有4種狀態(tài),級別從低到高依次是:無鎖、偏向鎖、輕量級鎖和重量級鎖。鎖狀態(tài)只能升級不能降級。如下:

圖片

無鎖

  • 無鎖沒有對資源進(jìn)行鎖定,所有的線程都能訪問并修改同一個資源,但同時只有一個線程能修改成功。
  • 無鎖的特點就是修改操作在循環(huán)內(nèi)進(jìn)行,線程會不斷的嘗試修改共享資源。如果沒有沖突就修改成功并退出,否則就會繼續(xù)循環(huán)嘗試。如果有多個線程修改同一個值,必定會有一個線程能修改成功,而其他修改失敗的線程會不斷重試直到修改成功。

偏向鎖

  • 偏向鎖是指一段同步代碼一直被一個線程所訪問,那么該線程會自動獲取鎖,降低獲取鎖的代價。
  • 引入偏向鎖是為了在無多線程競爭的情況下盡量減少不必要的輕量級鎖執(zhí)行路徑,其目標(biāo)就是在只有一個線程執(zhí)行同步代碼塊時能夠提高性能。
  • 當(dāng)一個線程訪問同步代碼塊并獲取鎖時,會在Mark Word里存儲鎖偏向的線程ID。在線程進(jìn)入和退出同步塊時不再通過CAS操作來加鎖和解鎖,而是檢測Mark Word里是否存儲著指向當(dāng)前線程的偏向鎖。
  • 偏向鎖只有遇到其他線程嘗試競爭偏向鎖時,持有偏向鎖的線程才會釋放鎖,線程不會主動釋放偏向鎖。偏向鎖的撤銷,需要等待全局安全點(在這個時間點上沒有字節(jié)碼正在執(zhí)行),它會首先暫停擁有偏向鎖的線程,判斷鎖對象是否處于被鎖定狀態(tài)。撤銷偏向鎖后恢復(fù)到無鎖或輕量級鎖的狀態(tài)。
  • 偏向鎖在JDK 6及以后的JVM里是默認(rèn)啟用的。可以通過JVM參數(shù)關(guān)閉偏向鎖: -XX:-UseBiasedLocking=false ,關(guān)閉之后程序默認(rèn)會進(jìn)入輕量級鎖狀態(tài)。

輕量級鎖

  • 是指當(dāng)鎖是偏向鎖的時候,被另外的線程所訪問,偏向鎖就會升級為輕量級鎖,其他線程會通過自旋的形式嘗試獲取鎖,不會阻塞,從而提高性能。
  • 在代碼進(jìn)入同步塊的時候,如果同步對象鎖狀態(tài)為無鎖狀態(tài)(鎖標(biāo)志位為01狀態(tài),是否為偏向鎖為0),虛擬機(jī)首先將在當(dāng)前線程的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用于存儲鎖對象目前的Mark Word的拷貝,然后拷貝對象頭中的Mark Word復(fù)制到鎖記錄中。
  • 拷貝成功后,虛擬機(jī)將使用CAS操作嘗試將對象的Mark Word更新為指向Lock Record的指針,并將Lock Record里的owner指針指向?qū)ο蟮腗ark Word。
  • 如果這個更新動作成功了,那么這個線程就擁有了該對象的鎖,并且對象Mark Word的鎖標(biāo)志位設(shè)置為00,表示此對象處于輕量級鎖定狀態(tài)。
  • 如果輕量級鎖的更新操作失敗了,虛擬機(jī)首先會檢查對象的Mark Word是否指向當(dāng)前線程的棧幀,如果是就說明當(dāng)前線程已經(jīng)擁有了這個對象的鎖,那就可以直接進(jìn)入同步塊繼續(xù)執(zhí)行,否則說明多個線程競爭鎖。

重量級鎖

  • 若當(dāng)前只有一個等待線程,則該線程通過自旋進(jìn)行等待。但是當(dāng)自旋超過一定的次數(shù),或者一個線程在持有鎖,一個在自旋,又有第三個來訪時,輕量級鎖升級為重量級鎖。
  • 升級為重量級鎖時,鎖標(biāo)志的狀態(tài)值變?yōu)?0,此時Mark Word中存儲的是指向重量級鎖的指針,此時等待鎖的線程都會進(jìn)入阻塞狀態(tài)。

綜上,偏向鎖通過對比Mark Word解決加鎖問題,避免執(zhí)行CAS操作。而輕量級鎖是通過用CAS操作和自旋來解決加鎖問題,避免線程阻塞和喚醒而影響性能。重量級鎖是將除了擁有鎖的線程以外的線程都阻塞。

四、synchronized存在的問題

1.性能損耗

  • 雖然在JDK 1.6中對synchronized做了很多優(yōu)化,如如適應(yīng)性自旋、鎖消除、鎖粗化、輕量級鎖和偏向鎖等,但畢竟還是一種鎖。
  • 所以,無論是使用同步方法還是同步代碼塊,在同步操作之前還是要進(jìn)行加鎖,同步操作之后需要進(jìn)行解鎖,這個加鎖、解鎖的過程是要有性能損耗的。

2. 阻塞

  • synchronize實現(xiàn)的鎖本質(zhì)上是一種阻塞鎖,多個線程要排隊訪問同一個共享對象。
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • JAVA語言
    +關(guān)注

    關(guān)注

    0

    文章

    138

    瀏覽量

    20076
  • JVM
    JVM
    +關(guān)注

    關(guān)注

    0

    文章

    157

    瀏覽量

    12209
  • 虛擬機(jī)
    +關(guān)注

    關(guān)注

    1

    文章

    908

    瀏覽量

    28095
  • CAS
    CAS
    +關(guān)注

    關(guān)注

    0

    文章

    34

    瀏覽量

    15183
收藏 人收藏

    評論

    相關(guān)推薦

    四種模擬輸入信號的保護(hù)電路實現(xiàn)方法

    本文介紹四種模擬輸入信號的保護(hù)電路的實現(xiàn)方法。
    發(fā)表于 03-28 09:55 ?1132次閱讀

    FPGA 設(shè)計的四種常用思想與技巧

    FPGA 設(shè)計的四種常用思想與技巧FPGA設(shè)計的四種常用思想與技巧 討論的四種常用FPGA/CPLD設(shè)計思想與技巧:乒乓操作、串并轉(zhuǎn)換、流水線操作、數(shù)據(jù)接口同步化,都是FPGA/CPLD 邏輯設(shè)計
    發(fā)表于 08-11 10:30

    四種無線充電技術(shù)簡單原理

    詳細(xì)介紹了電場耦合 電磁感應(yīng) 磁共振無線電波 這四種方式
    發(fā)表于 07-28 11:12

    四種不同供電模式的LED拓?fù)?b class='flag-5'>介紹

    本文中,小編將為大家介紹四種在LED供電當(dāng)中經(jīng)常使用的四種拓?fù)浣Y(jié)構(gòu)。感興趣的朋友快來看一看吧。 首先需要從了解轉(zhuǎn)換器的最小及最大輸出電壓入手。這只是將所有LED正向壓降與傳感電阻器電壓相加的總數(shù)
    發(fā)表于 10-10 15:07

    介紹UPS電源的四種工作方式

    UPS電源是較為常見的應(yīng)急電源系統(tǒng),其在市電正常與市電異常的情況下,工作方式也有所不同,以下介紹UPS電源的四種工作方式:正常運行、電池工作、旁路運行和旁路維護(hù)。1、正常運行方式 UPS電源系統(tǒng)
    發(fā)表于 11-16 06:19

    介紹AUTOSAR支持的四種功能安全機(jī)制

    1、AUTOSAR的四種功能安全機(jī)制雖然AUTOSAR不是一個完整的安全解決方案,但它提供了一些安全機(jī)制用于支持安全關(guān)鍵系統(tǒng)的開發(fā)。本文用于介紹AUTOSAR支持的四種功能安全機(jī)制:內(nèi)存分區(qū)
    發(fā)表于 06-10 17:33

    四種典型瞬態(tài)介紹

    ,我將介紹應(yīng)該注意的幾種典型瞬態(tài),以及TI如何幫助滿足瞬態(tài)保護(hù)需求。 瀏覽此文章,并查看參考設(shè)計:《汽車瞬態(tài)和過流保護(hù)濾波器參考設(shè)計》 典型瞬態(tài)在四種常見場景中可能會發(fā)生瞬變。圖1所示為第一場景
    發(fā)表于 11-07 08:02

    無線充電技術(shù)的四種方式及其原理和應(yīng)用介紹

    本文介紹了無線充電技術(shù)的應(yīng)用范圍及其電磁感應(yīng)方式等四種充電方式的詳細(xì)介紹
    發(fā)表于 10-12 16:16 ?27次下載
    無線充電技術(shù)的<b class='flag-5'>四種</b>方式及其原理和應(yīng)用<b class='flag-5'>介紹</b>

    jquery四種選擇器介紹

      本文給大家匯總介紹了jQuery的四種選擇器的使用方法以及示例,非常的簡單實用,希望對大家學(xué)習(xí)jquery能夠有所幫助。
    發(fā)表于 12-01 16:40 ?3032次閱讀
    jquery<b class='flag-5'>四種</b>選擇器<b class='flag-5'>介紹</b>

    四種溫度傳感器的數(shù)據(jù)介紹

    本文檔的主要內(nèi)容詳細(xì)介紹的是四種溫度傳感器的數(shù)據(jù)介紹包括了:  球形傳感器,卡箍式傳感器,地面?zhèn)鞲衅鳎秩胧絺鞲衅?/div>
    發(fā)表于 02-28 17:09 ?9次下載

    四種常見的圖像濾波算法介紹

    作者丨一支程序媛@知乎 來源丨h(huán)ttps://zhuanlan.zhihu.com/p/279602383 編輯丨極市平臺 導(dǎo)讀 圖像濾波是一非常重要的圖像處理技術(shù),本文詳細(xì)介紹四種常見的圖像
    的頭像 發(fā)表于 02-15 09:50 ?9923次閱讀

    四種方式實現(xiàn)led點亮

    四種方式實現(xiàn)led點亮
    發(fā)表于 01-04 14:31 ?4次下載

    NoSQL數(shù)據(jù)庫的四種類型

    在本文中,我們將簡要介紹NoSQL數(shù)據(jù)庫的四種類型。
    的頭像 發(fā)表于 04-25 17:21 ?4287次閱讀

    synchronized的鎖膨脹

    初識 synchronized 可以加在方法和類上面,作用于類和對象。下面代碼中列出了 synchronized用法。 public class SynchronizedTest
    的頭像 發(fā)表于 10-10 16:58 ?452次閱讀
    <b class='flag-5'>synchronized</b>的鎖膨脹

    介紹MCUboot支持的四種升級模式(2)

    介紹MCUboot支持的四種升級模式,分別是Overwrite、Swap、Direct XIP和加載到RAM中執(zhí)行。由于FSP不支持第四種——加載到RAM中執(zhí)行,因為我們重點介紹前三
    的頭像 發(fā)表于 06-13 10:56 ?771次閱讀
    <b class='flag-5'>介紹</b>MCUboot支持的<b class='flag-5'>四種</b>升級模式(2)