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

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

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

鎖實(shí)現(xiàn)的基本原理

Android編程精選 ? 來(lái)源:Android編程精選 ? 2023-05-29 10:11 ? 次閱讀

01 為什么要用鎖?

鎖-是為了解決并發(fā)操作引起的臟讀、數(shù)據(jù)不一致的問(wèn)題。

02 鎖實(shí)現(xiàn)的基本原理

2.1、volatile

Java編程語(yǔ)言允許線程訪問(wèn)共享變量, 為了確保共享變量能被準(zhǔn)確和一致地更新,線程應(yīng)該確保通過(guò)排他鎖單獨(dú)獲得這個(gè)變量。Java語(yǔ)言提供了volatile,在某些情況下比鎖要更加方便。

volatile在多處理器開(kāi)發(fā)中保證了共享變量的“ 可見(jiàn)性”??梢?jiàn)性的意思是當(dāng)一個(gè)線程修改一個(gè)共享變量時(shí),另外一個(gè)線程能讀到這個(gè)修改的值。

8bdf22e2-fd52-11ed-90ce-dac502259ad0.jpg202209192321367221.png

結(jié)論:如果volatile變量修飾符使用恰當(dāng)?shù)脑挘萻ynchronized的使用和執(zhí)行成本更低,因?yàn)樗粫?huì)引起線程上下文的切換和調(diào)度。

2.2、synchronized

synchronized通過(guò)鎖機(jī)制實(shí)現(xiàn)同步。

先來(lái)看下利用synchronized實(shí)現(xiàn)同步的基礎(chǔ):Java中的每一個(gè)對(duì)象都可以作為鎖。

具體表現(xiàn)為以下3種形式。

對(duì)于普通同步方法,鎖是當(dāng)前實(shí)例對(duì)象。

對(duì)于靜態(tài)同步方法,鎖是當(dāng)前類的Class對(duì)象。

對(duì)于同步方法塊,鎖是Synchonized括號(hào)里配置的對(duì)象。

當(dāng)一個(gè)線程試圖訪問(wèn)同步代碼塊時(shí),它首先必須得到鎖,退出或拋出異常時(shí)必須釋放鎖。

2.2.1 synchronized實(shí)現(xiàn)原理

synchronized是基于Monitor來(lái)實(shí)現(xiàn)同步的。

Monitor從兩個(gè)方面來(lái)支持線程之間的同步:

互斥執(zhí)行

協(xié)作

1、Java 使用對(duì)象鎖 ( 使用 synchronized 獲得對(duì)象鎖 ) 保證工作在共享的數(shù)據(jù)集上的線程互斥執(zhí)行。

2、使用 notify/notifyAll/wait 方法來(lái)協(xié)同不同線程之間的工作。

3、Class和Object都關(guān)聯(lián)了一個(gè)Monitor。

8bfd7490-fd52-11ed-90ce-dac502259ad0.jpg202209192321382042.png

Monitor 的工作機(jī)理

線程進(jìn)入同步方法中。

為了繼續(xù)執(zhí)行臨界區(qū)代碼,線程必須獲取 Monitor 鎖。如果獲取鎖成功,將成為該監(jiān)視者對(duì)象的擁有者。任一時(shí)刻內(nèi),監(jiān)視者對(duì)象只屬于一個(gè)活動(dòng)線程(The Owner)

擁有監(jiān)視者對(duì)象的線程可以調(diào)用 wait() 進(jìn)入等待集合(Wait Set),同時(shí)釋放監(jiān)視鎖,進(jìn)入等待狀態(tài)。

其他線程調(diào)用 notify() / notifyAll() 接口喚醒等待集合中的線程,這些等待的線程需要重新獲取監(jiān)視鎖后才能執(zhí)行 wait() 之后的代碼。

同步方法執(zhí)行完畢了,線程退出臨界區(qū),并釋放監(jiān)視鎖。

參考文檔:https://www.ibm.com/developerworks/cn/java/j-lo-synchronized

2.2.2 synchronized具體實(shí)現(xiàn)

1、同步代碼塊采用monitorenter、monitorexit指令顯式的實(shí)現(xiàn)。

2、同步方法則使用ACC_SYNCHRONIZED標(biāo)記符隱式的實(shí)現(xiàn)。

通過(guò)實(shí)例來(lái)看看具體實(shí)現(xiàn):

8c2173ea-fd52-11ed-90ce-dac502259ad0.jpg202209192321396333.png

javap編譯后的字節(jié)碼如下:

8c607a86-fd52-11ed-90ce-dac502259ad0.jpg202209192321405204.png

monitorenter

每一個(gè)對(duì)象都有一個(gè)monitor,一個(gè)monitor只能被一個(gè)線程擁有。當(dāng)一個(gè)線程執(zhí)行到monitorenter指令時(shí)會(huì)嘗試獲取相應(yīng)對(duì)象的monitor,獲取規(guī)則如下:

如果monitor的進(jìn)入數(shù)為0,則該線程可以進(jìn)入monitor,并將monitor進(jìn)入數(shù)設(shè)置為1,該線程即為monitor的擁有者。

如果當(dāng)前線程已經(jīng)擁有該monitor,只是重新進(jìn)入,則進(jìn)入monitor的進(jìn)入數(shù)加1,所以synchronized關(guān)鍵字實(shí)現(xiàn)的鎖是可重入的鎖。

如果monitor已被其他線程擁有,則當(dāng)前線程進(jìn)入阻塞狀態(tài),直到monitor的進(jìn)入數(shù)為0,再重新嘗試獲取monitor。

monitorexit

只有擁有相應(yīng)對(duì)象的monitor的線程才能執(zhí)行monitorexit指令。每執(zhí)行一次該指令monitor進(jìn)入數(shù)減1,當(dāng)進(jìn)入數(shù)為0時(shí)當(dāng)前線程釋放monitor,此時(shí)其他阻塞的線程將可以嘗試獲取該monitor。

2.2.3 鎖存放的位置

鎖標(biāo)記存放在Java對(duì)象頭的Mark Word中。

8c814018-fd52-11ed-90ce-dac502259ad0.jpg202209192321419575.png

Java對(duì)象頭長(zhǎng)度

8cb62260-fd52-11ed-90ce-dac502259ad0.jpg202209192321427846.png

32位JVM Mark Word 結(jié)構(gòu)

8cd20d68-fd52-11ed-90ce-dac502259ad0.jpg202209192321437957.png

32位JVM Mark Word 狀態(tài)變化

8d144872-fd52-11ed-90ce-dac502259ad0.jpg202209192321457398.png

64位JVM Mark Word 結(jié)構(gòu)

2.2.3 synchronized的鎖優(yōu)化

JavaSE1.6為了減少獲得鎖和釋放鎖帶來(lái)的性能消耗,引入了“偏向鎖”和“輕量級(jí)鎖”。

在JavaSE1.6中,鎖一共有4種狀態(tài),級(jí)別從低到高依次是:無(wú)鎖狀態(tài)、偏向鎖狀態(tài)、輕量級(jí)鎖狀態(tài)和重量級(jí)鎖狀態(tài),這幾個(gè)狀態(tài)會(huì)隨著競(jìng)爭(zhēng)情況逐漸升級(jí)。

鎖可以升級(jí)但不能降級(jí),意味著偏向鎖升級(jí)成輕量級(jí)鎖后不能降級(jí)成偏向鎖。這種鎖升級(jí)卻不能降級(jí)的策略,目的是為了提高獲得鎖和釋放鎖的效率。

偏向鎖:

無(wú)鎖競(jìng)爭(zhēng)的情況下為了減少鎖競(jìng)爭(zhēng)的資源開(kāi)銷,引入偏向鎖。

8d39aebe-fd52-11ed-90ce-dac502259ad0.jpg202209192321466679.png

輕量級(jí)鎖:

輕量級(jí)鎖所適應(yīng)的場(chǎng)景是線程交替執(zhí)行同步塊的情況。

8d39aebe-fd52-11ed-90ce-dac502259ad0.jpg2022091923214830110.png

鎖粗化 (Lock Coarsening):也就是減少不必要的緊連在一起的unlock,lock操作,將多個(gè)連續(xù)的鎖擴(kuò)展成一個(gè)范圍更大的鎖。

鎖消除 (Lock Elimination):鎖削除是指虛擬機(jī)即時(shí)編譯器在運(yùn)行時(shí),對(duì)一些代碼上要求同步,但是被檢測(cè)到不可能存在共享數(shù)據(jù)競(jìng)爭(zhēng)的鎖進(jìn)行削除。

適應(yīng)性自旋Adaptive Spinning):自適應(yīng)意味著自旋的時(shí)間不再固定了,而是由前一次在同一個(gè)鎖上的自旋時(shí)間及鎖的擁有者的狀態(tài)來(lái)決定。如果在同一個(gè)鎖對(duì)象上,自旋等待剛剛成功獲得過(guò)鎖,并且持有鎖的線程正在運(yùn)行中,那么虛擬機(jī)就會(huì)認(rèn)為這次自旋也很有可能再次成功,進(jìn)而它將允許自旋等待持續(xù)相對(duì)更長(zhǎng)的時(shí)間,比如100個(gè)循環(huán)。另一方面,如果對(duì)于某個(gè)鎖,自旋很少成功獲得過(guò),那在以后要獲取這個(gè)鎖時(shí)將可能省略掉自旋過(guò)程,以避免浪費(fèi)處理器資源。

2.2.4 鎖的優(yōu)缺點(diǎn)對(duì)比

8d855972-fd52-11ed-90ce-dac502259ad0.jpg2022091923214950111.png

2.3、CAS

CAS,在Java并發(fā)應(yīng)用中通常指CompareAndSwap或CompareAndSet,即比較并交換。

1、CAS是一個(gè)原子操作,它比較一個(gè)內(nèi)存位置的值并且只有相等時(shí)修改這個(gè)內(nèi)存位置的值為新的值,保證了新的值總是基于最新信息計(jì)算的,如果有其他線程在這期間修改了這個(gè)值則CAS失敗。CAS返回是否成功或者內(nèi)存位置原來(lái)的值用于判斷是否CAS成功。

2、JVM中的CAS操作是利用了處理器提供的CMPXCHG指令實(shí)現(xiàn)的。

優(yōu)點(diǎn):

競(jìng)爭(zhēng)不大的時(shí)候系統(tǒng)開(kāi)銷小。

缺點(diǎn):

循環(huán)時(shí)間長(zhǎng)開(kāi)銷大。

ABA問(wèn)題。

只能保證一個(gè)共享變量的原子操作。

03 Java中的鎖實(shí)現(xiàn)

3.1、隊(duì)列同步器(AQS)

隊(duì)列同步器AbstractQueuedSynchronizer(以下簡(jiǎn)稱同步器),是用來(lái)構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架。

3.1.1、它使用了一個(gè)int成員變量表示同步狀態(tài)。

8da42992-fd52-11ed-90ce-dac502259ad0.jpg2022091923215056212.png

3.1.2、通過(guò)內(nèi)置的FIFO雙向隊(duì)列來(lái)完成獲取鎖線程的排隊(duì)工作。

同步器包含兩個(gè)節(jié)點(diǎn)類型的應(yīng)用,一個(gè)指向頭節(jié)點(diǎn),一個(gè)指向尾節(jié)點(diǎn),未獲取到鎖的線程會(huì)創(chuàng)建節(jié)點(diǎn)線程安全(compareAndSetTail)的加入隊(duì)列尾部。同步隊(duì)列遵循FIFO,首節(jié)點(diǎn)是獲取同步狀態(tài)成功的節(jié)點(diǎn)。

8dc0509a-fd52-11ed-90ce-dac502259ad0.jpg2022091923215155913.png

未獲取到鎖的線程將創(chuàng)建一個(gè)節(jié)點(diǎn),設(shè)置到尾節(jié)點(diǎn)。如下圖所示:

8def1d12-fd52-11ed-90ce-dac502259ad0.jpg2022091923215254714.png

首節(jié)點(diǎn)的線程在釋放鎖時(shí),將會(huì)喚醒后繼節(jié)點(diǎn)。而后繼節(jié)點(diǎn)將會(huì)在獲取鎖成功時(shí)將自己設(shè)置為首節(jié)點(diǎn)。如下圖所示:

8e0e3d14-fd52-11ed-90ce-dac502259ad0.jpg2022091923215350515.png

3.1.3、獨(dú)占式/共享式鎖獲取

獨(dú)占式:有且只有一個(gè)線程能獲取到鎖,如:ReentrantLock;

共享式:可以多個(gè)線程同時(shí)獲取到鎖,如:CountDownLatch;

獨(dú)占式

每個(gè)節(jié)點(diǎn)自旋觀察自己的前一節(jié)點(diǎn)是不是Header節(jié)點(diǎn),如果是,就去嘗試獲取鎖。

8e49e8f0-fd52-11ed-90ce-dac502259ad0.jpg2022091923215449316.png

獨(dú)占式鎖獲取流程:

8e6dfede-fd52-11ed-90ce-dac502259ad0.jpg2022091923215549017.png

共享式:

共享式與獨(dú)占式的區(qū)別:

8ebfc534-fd52-11ed-90ce-dac502259ad0.jpg2022091923215682218.png

共享鎖獲取流程:

8ee1abe0-fd52-11ed-90ce-dac502259ad0.jpg2022091923215785919.png

審核編輯:彭靜
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 處理器
    +關(guān)注

    關(guān)注

    68

    文章

    19286

    瀏覽量

    229865
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    7035

    瀏覽量

    89045
  • 編程語(yǔ)言
    +關(guān)注

    關(guān)注

    10

    文章

    1945

    瀏覽量

    34745

原文標(biāo)題:Java中的鎖原理、鎖優(yōu)化、CAS、AQS,看這篇就對(duì)了!

文章出處:【微信號(hào):AndroidPush,微信公眾號(hào):Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    步進(jìn)電機(jī)基本原理

    本帖最后由 eehome 于 2013-1-5 09:48 編輯 步進(jìn)電機(jī)基本原理
    發(fā)表于 08-16 16:17

    串聯(lián)諧振逆變器的基本原理

    串聯(lián)諧振通常伴有逆變器。該組合稱為串聯(lián)諧振逆變器。什么是基本原理?讓我簡(jiǎn)要介紹串聯(lián)諧振逆變器的一些基本原理。]首先給你看一張圖片:
    發(fā)表于 11-07 10:21

    電流檢測(cè)電阻的基本原理

    電流檢測(cè)電阻的基本原理簡(jiǎn)單采樣電路的實(shí)現(xiàn)
    發(fā)表于 01-29 06:26

    電機(jī)轉(zhuǎn)動(dòng)的基本原理是什么?

    電機(jī)轉(zhuǎn)動(dòng)的基本原理是什么?電機(jī)運(yùn)動(dòng)的基本原則有哪些?
    發(fā)表于 07-21 07:59

    線性電源的基本原理是什么

    多路線性電源 AC-DC穩(wěn)壓電源 低紋波電源 可調(diào)線性電源 原理圖PCB目錄多路線性電源 AC-DC穩(wěn)壓電源 低紋波電源 可調(diào)線性電源 原理圖PCB基本原理芯片選型原理圖&3D-PCB具體
    發(fā)表于 07-30 07:47

    無(wú)線充電的基本原理是什么

    一 、無(wú)線充電基本原理無(wú)線充電的基本原理就是我們平時(shí)常用的開(kāi)關(guān)電源原理,區(qū)別在于沒(méi)有磁介質(zhì)耦合,那么我們需要利用磁共振的方式提高耦合效率,具體方法是在發(fā)送端和接收端線圈串并聯(lián)電容,是發(fā)送線圈處理諧振
    發(fā)表于 09-15 06:01

    RAID技術(shù)的基本原理是什么

    RAID技術(shù)的基本原理是什么?RAID技術(shù)有哪幾個(gè)優(yōu)勢(shì)?
    發(fā)表于 10-14 12:01

    PWM控制的基本原理是什么

    PWM控制的基本原理是什么?如何讓逆變器輸出為等幅矩形波呢?如何實(shí)現(xiàn)低通濾波器呢?
    發(fā)表于 10-22 07:06

    DA0832實(shí)現(xiàn)DA轉(zhuǎn)換的基本原理是什么

    1、描述DA0832實(shí)現(xiàn)DA轉(zhuǎn)換的基本原理1、DAC0832是一個(gè)八位D/A轉(zhuǎn)換器,D/A轉(zhuǎn)換電路是一個(gè)R-2RT型電阻網(wǎng)絡(luò),可實(shí)現(xiàn)8位數(shù)據(jù)的轉(zhuǎn)換。2、數(shù)據(jù)總線——DI7~DI0:轉(zhuǎn)換數(shù)據(jù)輸入控制
    發(fā)表于 11-26 08:27

    串口通信的基本原理是什么?

    同步通信和異步通信的區(qū)別是什么?串口通信的基本原理是什么?
    發(fā)表于 12-13 06:46

    PDB基本原理是什么

    PDB基本原理1.Programmable delay block (PDB)的邏輯框圖2.核心功能實(shí)現(xiàn)2.1.輸入trigger的選擇2.2.trigger和pre-trigger的輸出
    發(fā)表于 01-13 08:29

    步進(jìn)馬達(dá)基本原理

    步進(jìn)馬達(dá)基本原理步進(jìn)馬達(dá)基本原理步進(jìn)馬達(dá)基本原理
    發(fā)表于 11-30 11:55 ?8次下載

    Redis Cluster的基本原理實(shí)現(xiàn)細(xì)節(jié)

    Redis Cluster的基本原理和架構(gòu) Redis Cluster是分布式Redis的實(shí)現(xiàn)。隨著Redis版本的更替,以及各種已知bug的fixed,在穩(wěn)定性和高可用性上有了很大的提升和進(jìn)步
    發(fā)表于 09-28 19:09 ?0次下載
    Redis Cluster的<b class='flag-5'>基本原理</b>及<b class='flag-5'>實(shí)現(xiàn)</b>細(xì)節(jié)

    分布式基本原理和案例實(shí)現(xiàn)

    前面我們有聊過(guò)樂(lè)觀和悲觀實(shí)現(xiàn),均是對(duì)于單體架構(gòu)的場(chǎng)景下的實(shí)現(xiàn)。那么現(xiàn)在我們來(lái)總結(jié)看下分布式情況下如何實(shí)現(xiàn)
    的頭像 發(fā)表于 07-01 14:53 ?3339次閱讀
    分布式<b class='flag-5'>鎖</b>的<b class='flag-5'>基本原理</b>和案例<b class='flag-5'>實(shí)現(xiàn)</b>

    RNN的基本原理實(shí)現(xiàn)

    RNN,即循環(huán)神經(jīng)網(wǎng)絡(luò)(Recurrent Neural Network),是一種特殊類型的人工神經(jīng)網(wǎng)絡(luò),專門設(shè)計(jì)用于處理序列數(shù)據(jù),如文本、語(yǔ)音、視頻等。以下是對(duì)RNN基本原理實(shí)現(xiàn)的介紹: 一
    的頭像 發(fā)表于 11-15 09:49 ?439次閱讀