一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

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

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

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

聊聊Java中那18把鎖

jf_ro2CN3Fa ? 來(lái)源:愛(ài)笑的架構(gòu)師 ? 2023-05-15 09:44 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

樂(lè)觀鎖和悲觀鎖

悲觀鎖

悲觀鎖對(duì)應(yīng)于生活中悲觀的人,悲觀的人總是想著事情往壞的方向發(fā)展。

舉個(gè)生活中的例子,假設(shè)廁所只有一個(gè)坑位了,悲觀鎖上廁所會(huì)第一時(shí)間把門(mén)反鎖上,這樣其他人上廁所只能在門(mén)外等候,這種狀態(tài)就是「阻塞」了。

回到代碼世界中,一個(gè)共享數(shù)據(jù)加了悲觀鎖,那線程每次想操作這個(gè)數(shù)據(jù)前都會(huì)假設(shè)其他線程也可能會(huì)操作這個(gè)數(shù)據(jù),所以每次操作前都會(huì)上鎖,這樣其他線程想操作這個(gè)數(shù)據(jù)拿不到鎖只能阻塞了。

8a552608-f28d-11ed-90ce-dac502259ad0.png

Java 語(yǔ)言中 synchronized 和 ReentrantLock等就是典型的悲觀鎖,還有一些使用了 synchronized 關(guān)鍵字的容器類如 HashTable 等也是悲觀鎖的應(yīng)用。

樂(lè)觀鎖

樂(lè)觀鎖 對(duì)應(yīng)于生活中樂(lè)觀的人,樂(lè)觀的人總是想著事情往好的方向發(fā)展。

舉個(gè)生活中的例子,假設(shè)廁所只有一個(gè)坑位了,樂(lè)觀鎖認(rèn)為:這荒郊野外的,又沒(méi)有什么人,不會(huì)有人搶我坑位的,每次關(guān)門(mén)上鎖多浪費(fèi)時(shí)間,還是不加鎖好了。你看樂(lè)觀鎖就是天生樂(lè)觀!

回到代碼世界中,樂(lè)觀鎖操作數(shù)據(jù)時(shí)不會(huì)上鎖,在更新的時(shí)候會(huì)判斷一下在此期間是否有其他線程去更新這個(gè)數(shù)據(jù)。

8a776b28-f28d-11ed-90ce-dac502259ad0.png

樂(lè)觀鎖可以使用版本號(hào)機(jī)制和CAS算法實(shí)現(xiàn)。在 Java 語(yǔ)言中 java.util.concurrent.atomic包下的原子類就是使用CAS 樂(lè)觀鎖實(shí)現(xiàn)的。

兩種鎖的使用場(chǎng)景

悲觀鎖和樂(lè)觀鎖沒(méi)有孰優(yōu)孰劣,有其各自適應(yīng)的場(chǎng)景。

樂(lè)觀鎖適用于寫(xiě)比較少(沖突比較?。┑膱?chǎng)景,因?yàn)椴挥蒙湘i、釋放鎖,省去了鎖的開(kāi)銷,從而提升了吞吐量。

如果是寫(xiě)多讀少的場(chǎng)景,即沖突比較嚴(yán)重,線程間競(jìng)爭(zhēng)激勵(lì),使用樂(lè)觀鎖就是導(dǎo)致線程不斷進(jìn)行重試,這樣可能還降低了性能,這種場(chǎng)景下使用悲觀鎖就比較合適。

獨(dú)占鎖和共享鎖

獨(dú)占鎖

獨(dú)占鎖是指鎖一次只能被一個(gè)線程所持有。如果一個(gè)線程對(duì)數(shù)據(jù)加上排他鎖后,那么其他線程不能再對(duì)該數(shù)據(jù)加任何類型的鎖。獲得獨(dú)占鎖的線程即能讀數(shù)據(jù)又能修改數(shù)據(jù)。

8a9dbd96-f28d-11ed-90ce-dac502259ad0.png

JDK中的synchronized和java.util.concurrent(JUC)包中Lock的實(shí)現(xiàn)類就是獨(dú)占鎖。

共享鎖

共享鎖是指鎖可被多個(gè)線程所持有。如果一個(gè)線程對(duì)數(shù)據(jù)加上共享鎖后,那么其他線程只能對(duì)數(shù)據(jù)再加共享鎖,不能加獨(dú)占鎖。獲得共享鎖的線程只能讀數(shù)據(jù),不能修改數(shù)據(jù)。

8ab172d2-f28d-11ed-90ce-dac502259ad0.png

在 JDK 中 ReentrantReadWriteLock 就是一種共享鎖。

互斥鎖和讀寫(xiě)鎖

互斥鎖

互斥鎖是獨(dú)占鎖的一種常規(guī)實(shí)現(xiàn),是指某一資源同時(shí)只允許一個(gè)訪問(wèn)者對(duì)其進(jìn)行訪問(wèn),具有唯一性和排它性。

8acdf6b4-f28d-11ed-90ce-dac502259ad0.png

互斥鎖一次只能一個(gè)線程擁有互斥鎖,其他線程只有等待。

讀寫(xiě)鎖

讀寫(xiě)鎖是共享鎖的一種具體實(shí)現(xiàn)。讀寫(xiě)鎖管理一組鎖,一個(gè)是只讀的鎖,一個(gè)是寫(xiě)鎖。

讀鎖可以在沒(méi)有寫(xiě)鎖的時(shí)候被多個(gè)線程同時(shí)持有,而寫(xiě)鎖是獨(dú)占的。寫(xiě)鎖的優(yōu)先級(jí)要高于讀鎖,一個(gè)獲得了讀鎖的線程必須能看到前一個(gè)釋放的寫(xiě)鎖所更新的內(nèi)容。

讀寫(xiě)鎖相比于互斥鎖并發(fā)程度更高,每次只有一個(gè)寫(xiě)線程,但是同時(shí)可以有多個(gè)線程并發(fā)讀。

8ae9dca8-f28d-11ed-90ce-dac502259ad0.png

在 JDK 中定義了一個(gè)讀寫(xiě)鎖的接口:ReadWriteLock

publicinterfaceReadWriteLock{
/**
*獲取讀鎖
*/
LockreadLock();

/**
*獲取寫(xiě)鎖
*/
LockwriteLock();
}

ReentrantReadWriteLock 實(shí)現(xiàn)了ReadWriteLock接口,具體實(shí)現(xiàn)這里不展開(kāi),后續(xù)會(huì)深入源碼解析。

公平鎖和非公平鎖

公平鎖

公平鎖是指多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖,這里類似排隊(duì)買(mǎi)票,先來(lái)的人先買(mǎi),后來(lái)的人在隊(duì)尾排著,這是公平的。

8b01e758-f28d-11ed-90ce-dac502259ad0.png

在 java 中可以通過(guò)構(gòu)造函數(shù)初始化公平鎖

/**
*創(chuàng)建一個(gè)可重入鎖,true 表示公平鎖,false 表示非公平鎖。默認(rèn)非公平鎖
*/
Locklock=newReentrantLock(true);

非公平鎖

非公平鎖是指多個(gè)線程獲取鎖的順序并不是按照申請(qǐng)鎖的順序,有可能后申請(qǐng)的線程比先申請(qǐng)的線程優(yōu)先獲取鎖,在高并發(fā)環(huán)境下,有可能造成優(yōu)先級(jí)翻轉(zhuǎn),或者饑餓的狀態(tài)(某個(gè)線程一直得不到鎖)。

8b124530-f28d-11ed-90ce-dac502259ad0.png

在 java 中 synchronized 關(guān)鍵字是非公平鎖,ReentrantLock默認(rèn)也是非公平鎖。

/**
*創(chuàng)建一個(gè)可重入鎖,true 表示公平鎖,false 表示非公平鎖。默認(rèn)非公平鎖
*/
Locklock=newReentrantLock(false);

可重入鎖

可重入鎖又稱之為遞歸鎖,是指同一個(gè)線程在外層方法獲取了鎖,在進(jìn)入內(nèi)層方法會(huì)自動(dòng)獲取鎖。

8b2dab04-f28d-11ed-90ce-dac502259ad0.png

對(duì)于Java ReentrantLock而言, 他的名字就可以看出是一個(gè)可重入鎖。對(duì)于Synchronized而言,也是一個(gè)可重入鎖。

敲黑板:可重入鎖的一個(gè)好處是可一定程度避免死鎖。

以 synchronized 為例,看一下下面的代碼:

publicsynchronizedvoidmehtodA()throwsException{
//Dosomemagictings
mehtodB();
}

publicsynchronizedvoidmehtodB()throwsException{
//Dosomemagictings
}

上面的代碼中 methodA 調(diào)用 methodB,如果一個(gè)線程調(diào)用methodA 已經(jīng)獲取了鎖再去調(diào)用 methodB 就不需要再次獲取鎖了,這就是可重入鎖的特性。如果不是可重入鎖的話,mehtodB 可能不會(huì)被當(dāng)前線程執(zhí)行,可能造成死鎖。

自旋鎖

自旋鎖是指線程在沒(méi)有獲得鎖時(shí)不是被直接掛起,而是執(zhí)行一個(gè)忙循環(huán),這個(gè)忙循環(huán)就是所謂的自旋。

8b526304-f28d-11ed-90ce-dac502259ad0.png

自旋鎖的目的是為了減少線程被掛起的幾率,因?yàn)榫€程的掛起和喚醒也都是耗資源的操作。

如果鎖被另一個(gè)線程占用的時(shí)間比較長(zhǎng),即使自旋了之后當(dāng)前線程還是會(huì)被掛起,忙循環(huán)就會(huì)變成浪費(fèi)系統(tǒng)資源的操作,反而降低了整體性能。因此自旋鎖是不適應(yīng)鎖占用時(shí)間長(zhǎng)的并發(fā)情況的。

在 Java 中,AtomicInteger 類有自旋的操作,我們看一下代碼:

publicfinalintgetAndAddInt(Objecto,longoffset,intdelta){
intv;
do{
v=getIntVolatile(o,offset);
}while(!compareAndSwapInt(o,offset,v,v+delta));
returnv;
}

CAS 操作如果失敗就會(huì)一直循環(huán)獲取當(dāng)前 value 值然后重試。

另外自適應(yīng)自旋鎖也需要了解一下。

在JDK1.6又引入了自適應(yīng)自旋,這個(gè)就比較智能了,自旋時(shí)間不再固定,由前一次在同一個(gè)鎖上的自旋時(shí)間以及鎖的擁有者的狀態(tài)來(lái)決定。如果虛擬機(jī)認(rèn)為這次自旋也很有可能再次成功那就會(huì)次序較多的時(shí)間,如果自旋很少成功,那以后可能就直接省略掉自旋過(guò)程,避免浪費(fèi)處理器資源。

分段鎖

分段鎖 是一種鎖的設(shè)計(jì),并不是具體的一種鎖。

分段鎖設(shè)計(jì)目的是將鎖的粒度進(jìn)一步細(xì)化,當(dāng)操作不需要更新整個(gè)數(shù)組的時(shí)候,就僅僅針對(duì)數(shù)組中的一項(xiàng)進(jìn)行加鎖操作。

8b796b98-f28d-11ed-90ce-dac502259ad0.png

在 Java 語(yǔ)言中 CurrentHashMap 底層就用了分段鎖,使用Segment,就可以進(jìn)行并發(fā)使用了。

鎖升級(jí)(無(wú)鎖|偏向鎖|輕量級(jí)鎖|重量級(jí)鎖)

JDK1.6 為了提升性能減少獲得鎖和釋放鎖所帶來(lái)的消耗,引入了4種鎖的狀態(tài):無(wú)鎖、偏向鎖、輕量級(jí)鎖和重量級(jí)鎖,它會(huì)隨著多線程的競(jìng)爭(zhēng)情況逐漸升級(jí),但不能降級(jí)。

無(wú)鎖

無(wú)鎖狀態(tài)其實(shí)就是上面講的樂(lè)觀鎖,這里不再贅述。

偏向鎖

Java偏向鎖(Biased Locking)是指它會(huì)偏向于第一個(gè)訪問(wèn)鎖的線程,如果在運(yùn)行過(guò)程中,只有一個(gè)線程訪問(wèn)加鎖的資源,不存在多線程競(jìng)爭(zhēng)的情況,那么線程是不需要重復(fù)獲取鎖的,這種情況下,就會(huì)給線程加一個(gè)偏向鎖。

偏向鎖的實(shí)現(xiàn)是通過(guò)控制對(duì)象Mark Word的標(biāo)志位來(lái)實(shí)現(xiàn)的,如果當(dāng)前是可偏向狀態(tài),需要進(jìn)一步判斷對(duì)象頭存儲(chǔ)的線程 ID 是否與當(dāng)前線程 ID 一致,如果一致直接進(jìn)入。

輕量級(jí)鎖

當(dāng)線程競(jìng)爭(zhēng)變得比較激烈時(shí),偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖,輕量級(jí)鎖認(rèn)為雖然競(jìng)爭(zhēng)是存在的,但是理想情況下競(jìng)爭(zhēng)的程度很低,通過(guò)自旋方式等待上一個(gè)線程釋放鎖。

重量級(jí)鎖

如果線程并發(fā)進(jìn)一步加劇,線程的自旋超過(guò)了一定次數(shù),或者一個(gè)線程持有鎖,一個(gè)線程在自旋,又來(lái)了第三個(gè)線程訪問(wèn)時(shí)(反正就是競(jìng)爭(zhēng)繼續(xù)加大了),輕量級(jí)鎖就會(huì)膨脹為重量級(jí)鎖,重量級(jí)鎖會(huì)使除了此時(shí)擁有鎖的線程以外的線程都阻塞。

升級(jí)到重量級(jí)鎖其實(shí)就是互斥鎖了,一個(gè)線程拿到鎖,其余線程都會(huì)處于阻塞等待狀態(tài)。

在 Java 中,synchronized 關(guān)鍵字內(nèi)部實(shí)現(xiàn)原理就是鎖升級(jí)的過(guò)程:無(wú)鎖 --> 偏向鎖 --> 輕量級(jí)鎖 --> 重量級(jí)鎖。這一過(guò)程在后續(xù)講解 synchronized 關(guān)鍵字的原理時(shí)會(huì)詳細(xì)介紹。

鎖優(yōu)化技術(shù)(鎖粗化、鎖消除)

鎖粗化

鎖粗化就是將多個(gè)同步塊的數(shù)量減少,并將單個(gè)同步塊的作用范圍擴(kuò)大,本質(zhì)上就是將多次上鎖、解鎖的請(qǐng)求合并為一次同步請(qǐng)求。

舉個(gè)例子,一個(gè)循環(huán)體中有一個(gè)代碼同步塊,每次循環(huán)都會(huì)執(zhí)行加鎖解鎖操作。

privatestaticfinalObjectLOCK=newObject();

for(inti=0;i

經(jīng)過(guò)鎖粗化后就變成下面這個(gè)樣子了:

synchronized(LOCK){
for(inti=0;i

鎖消除

鎖消除是指虛擬機(jī)編譯器在運(yùn)行時(shí)檢測(cè)到了共享數(shù)據(jù)沒(méi)有競(jìng)爭(zhēng)的鎖,從而將這些鎖進(jìn)行消除。

舉個(gè)例子讓大家更好理解。

publicStringtest(Strings1,Strings2){
StringBufferstringBuffer=newStringBuffer();
stringBuffer.append(s1);
stringBuffer.append(s2);
returnstringBuffer.toString();
}

上面代碼中有一個(gè) test 方法,主要作用是將字符串 s1 和字符串 s2 串聯(lián)起來(lái)。

test 方法中三個(gè)變量s1, s2, stringBuffer, 它們都是局部變量,局部變量是在棧上的,棧是線程私有的,所以就算有多個(gè)線程訪問(wèn) test 方法也是線程安全的。

我們都知道 StringBuffer 是線程安全的類,append 方法是同步方法,但是 test 方法本來(lái)就是線程安全的,為了提升效率,虛擬機(jī)幫我們消除了這些同步鎖,這個(gè)過(guò)程就被稱為鎖消除。

StringBuffer.class

//append是同步方法
publicsynchronizedStringBufferappend(Stringstr){
toStringCache=null;
super.append(str);
returnthis;
}

一張圖總結(jié):

前面講了 Java 語(yǔ)言中各種各種的鎖,最后再通過(guò)六個(gè)問(wèn)題統(tǒng)一總結(jié)一下:

8b8c537a-f28d-11ed-90ce-dac502259ad0.png






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(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

    文章

    19896

    瀏覽量

    235220
  • JAVA語(yǔ)言
    +關(guān)注

    關(guān)注

    0

    文章

    138

    瀏覽量

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

    關(guān)注

    0

    文章

    35

    瀏覽量

    15409

原文標(biāo)題:聊聊Java中那18 把鎖

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    Java 利用 redis 實(shí)現(xiàn)一個(gè)分布式服務(wù)

    Java 利用 redis 實(shí)現(xiàn)一個(gè)分布式服務(wù)
    發(fā)表于 07-05 13:14

    聊聊字符串

    大家好,我是驚覺(jué),今天聊聊字符串。字符串的使用場(chǎng)景非常之多,人機(jī)交互和雙機(jī)通信都會(huì)用到。比如:通過(guò)串口向單片機(jī)發(fā)送指令,以執(zhí)行操作或配置參數(shù)。單片機(jī)讀取傳感器數(shù)據(jù),數(shù)據(jù)格式是字符串。一般GPS數(shù)據(jù)
    發(fā)表于 02-28 06:52

    如何給筆記本上一U

    如何給筆記本上一U   數(shù)據(jù)往往超過(guò)筆記本的價(jià)值   2010年的今天,筆記本電腦在我們工作的重要性與日聚
    發(fā)表于 01-23 11:14 ?1075次閱讀

    聊聊java泛型實(shí)現(xiàn)的原理與好處

    取得自己泛型參數(shù)的Class類型,C++只能由編譯器推斷在不為人知的地方生成新的類,對(duì)于特定的模板參數(shù)你只能使用特化。在本文中我主要想聊聊泛型的實(shí)現(xiàn)原理和一些高級(jí)特性。 泛型基礎(chǔ) 泛型是對(duì)Java語(yǔ)言類型系統(tǒng)的一種擴(kuò)展,有點(diǎn)類似于C++的模板,可以
    發(fā)表于 09-27 16:50 ?0次下載

    java數(shù)組的三種定義方式_java數(shù)組的定義及使用方法(推薦)

    java,數(shù)組是一種很常用的工具,本文將介紹來(lái)java數(shù)組的三種定義方式以及java數(shù)組的
    發(fā)表于 01-29 09:53 ?3.3w次閱讀

    java學(xué)習(xí)——java面試【事務(wù)、、多線程】資料整理

    本文檔內(nèi)容介紹了基于java學(xué)習(xí)java面試【事務(wù)、、多線程】資料整理,供參考
    發(fā)表于 03-13 13:53 ?0次下載

    java學(xué)習(xí)——java的反射學(xué)習(xí)筆記

    本文檔內(nèi)容介紹了java學(xué)習(xí)java的反射學(xué)習(xí)筆記,供參考
    發(fā)表于 03-13 14:19 ?0次下載

    如何挑放心

    第一招:買(mǎi)看鑰匙的“牙齒”。牙越多,齒越深,排列越復(fù)雜,開(kāi)啟難度越大,防盜性能越好。第二招:挑論斤稱兩,一比重量二看芯。重量越重,芯質(zhì)量越好,
    發(fā)表于 08-22 11:02 ?351次閱讀

    Java學(xué)習(xí)路線教程之Java新手必須學(xué)習(xí)21個(gè)技術(shù)點(diǎn)詳細(xì)資料說(shuō)明

    Java調(diào)用本地接口方法,一般用于C/C++代碼的調(diào)用。需要注意的是在java中加載so/dll文件的路徑問(wèn)題,本身調(diào)用接口并不復(fù)雜,但是經(jīng)常在是否加載了所需的本地接口庫(kù)花費(fèi)較多時(shí)
    發(fā)表于 12-19 15:23 ?19次下載

    如果智能不防鉆 安全一定無(wú)法保證

    安全專家提示:1、如果芯不防鉆,那一定不是C級(jí)芯。因?yàn)閲?guó)家標(biāo)準(zhǔn)對(duì)C級(jí)芯對(duì)防鉆的要求非常明確,防鉆需要三十分鐘。按這標(biāo)準(zhǔn),芯如果沒(méi)有防鉆功能、沒(méi)有防鉆的保護(hù)
    發(fā)表于 01-17 15:17 ?945次閱讀

    小區(qū)業(yè)主自制66的門(mén)禁系統(tǒng)

    12月16日,遼寧沈陽(yáng)的皇姑區(qū)一小區(qū)上了熱搜。 ? 原因竟然是這個(gè)小區(qū)的業(yè)主自制66的門(mén)禁系統(tǒng),被網(wǎng)友譽(yù)為“最便宜的門(mén)禁系統(tǒng)”。 ? 據(jù)報(bào)道,該小區(qū)之前總有外來(lái)車輛進(jìn)出,停車位被二手車行霸占
    的頭像 發(fā)表于 01-06 17:11 ?2999次閱讀

    Java時(shí)間戳的使用

    Java時(shí)間戳的使用
    的頭像 發(fā)表于 11-06 16:04 ?516次閱讀
    <b class='flag-5'>Java</b><b class='flag-5'>中</b>時(shí)間戳的使用

    如何使用 Java 原生格式化Instant

    今天我們將聊聊如何在Java一個(gè) Instant 格式化為一個(gè)字符串。我們將展示如何使用 Java 原生和第三方庫(kù)(如Joda-Time
    的頭像 發(fā)表于 10-09 14:51 ?1651次閱讀

    Java redis怎么實(shí)現(xiàn)

    Java實(shí)現(xiàn)Redis涉及到以下幾個(gè)方面:Redis的安裝配置、Redis連接池的使用、Redis數(shù)據(jù)結(jié)構(gòu)的選擇、實(shí)現(xiàn)分布式的幾種方式等。 一、Redis的安裝配置 下載Red
    的頭像 發(fā)表于 12-04 10:47 ?1479次閱讀

    java redis處理并發(fā)代碼

    在并發(fā)編程,一個(gè)常見(jiàn)的問(wèn)題是如何確保多個(gè)線程安全地訪問(wèn)共享資源,避免產(chǎn)生競(jìng)態(tài)條件和數(shù)據(jù)異常。而Redis作為一種高性能的內(nèi)存數(shù)據(jù)庫(kù),可以提供分布式的功能,通過(guò)Redis,我們可以有效地解決并發(fā)
    的頭像 發(fā)表于 12-04 11:04 ?1223次閱讀