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

Java中復(fù)雜的Synchronized關(guān)鍵字使用方法詳解

 更新時(shí)間:2024年01月28日 11:50:53   作者:小熊愛吃軟糖吖  
Synchronized關(guān)鍵字是一個(gè)種鎖,其有很多名字,例如重量級(jí)鎖、悲觀鎖、可重入鎖、、非公平、對(duì)象鎖等等,這篇文章主要給大家介紹了關(guān)于Java中復(fù)雜的Synchronized關(guān)鍵字使用方法的相關(guān)資料,需要的朋友可以參考下

 一、synchronized的特性

(1)互斥

synchronized 通過(guò)互斥達(dá)到原子性(線程安全的四大特性之一)

synchronized 會(huì)起到互斥效果,某個(gè)線程執(zhí)行到某個(gè)對(duì)象的 synchronized 中時(shí),其他線程如果也執(zhí)行到同一個(gè)對(duì)象 synchronized 就會(huì)阻塞等待。同一時(shí)間,只能一個(gè)線程擁有這把鎖,去執(zhí)行代碼。

1.進(jìn)入 synchronized 修飾的代碼塊, 相當(dāng)于 加鎖。

2.退出 synchronized 修飾的代碼塊, 相當(dāng)于 解鎖。

synchronized void increase(){//進(jìn)入方法內(nèi)部,相當(dāng)于針對(duì)當(dāng)前對(duì)象 
        count++;

}//執(zhí)行完畢相當(dāng)于針對(duì)當(dāng)前對(duì)象“解鎖”

synchronized用的鎖是存在Java對(duì)象頭里的。synchronized的底層是使用操作系統(tǒng)的mutex lock實(shí)現(xiàn)的。

可以粗略理解成, 每個(gè)對(duì)象在內(nèi)存中存儲(chǔ)的時(shí)候, 都存有一塊內(nèi)存表示當(dāng)前的 "鎖定" 狀態(tài)。如果當(dāng)前是 "未鎖定" 狀態(tài), 那么就可以使用, 使用時(shí)需要設(shè)為 "鎖定" 狀態(tài)。如果當(dāng)前是 "鎖定" 狀態(tài), 那么其他人無(wú)法使用, 只能排隊(duì)等待。一個(gè)線程先上了鎖,其他線程只能等待這個(gè)線程釋放。

注意點(diǎn):

針對(duì)每一把鎖,操作系統(tǒng)內(nèi)部都維護(hù)了一個(gè)等待隊(duì)列。當(dāng)這個(gè)鎖被某個(gè)線程占有的時(shí)候, 其他線程嘗試進(jìn)行加鎖,就加不上了, 就會(huì)阻塞等待, 一直等到之前的線程解鎖之后,由操作系統(tǒng)喚醒一個(gè)新的線程,再來(lái)獲取到這個(gè)鎖。

上一個(gè)線程解鎖之后, 下一個(gè)線程并不是立即就能獲取到鎖。而是要靠操作系統(tǒng)來(lái) "喚醒"。 這也就是操作系統(tǒng)線程調(diào)度的一部分工作。

假設(shè)有 A B C 三個(gè)線程,線程 A 先獲取到鎖,然后 B 嘗試獲取鎖, 然后 C 再嘗試獲取鎖,此時(shí) B和 C 都在阻塞隊(duì)列中排隊(duì)等待。 但是當(dāng) A 釋放鎖之后, 雖然 B 比 C 先來(lái)的, 但是 B 不一定就能獲取到鎖, 而是和 C 重新競(jìng)爭(zhēng),并不遵守先來(lái)后到的規(guī)則

(2)刷新內(nèi)存

synchronized 通過(guò)加鎖減鎖能保證內(nèi)存可見性。

synchronized 的工作過(guò)程:

1. 獲得互斥鎖

2. 從主內(nèi)存拷貝變量的最新副本到工作的內(nèi)存

3. 執(zhí)行代碼

4. 將更改后的共享變量的值刷新到主內(nèi)存

5. 釋放互斥鎖

所以 synchronized 也能保證內(nèi)存可見性。

(3)可重入

synchronized 只能有一定約束,并不能完全禁止指令重排序。synchronized 同步塊對(duì)同一條線程來(lái)說(shuō)是可重入的,不會(huì)出現(xiàn)自己把自己鎖死的問(wèn)題。

// 第一次加鎖, 加鎖成功
lock();
// 第二次加鎖, 鎖已經(jīng)被占用, 阻塞等待.
lock();

對(duì)于把自己鎖死,就是一個(gè)線程沒(méi)有釋放鎖,然后又嘗試再次加鎖。

按照之前對(duì)于鎖的設(shè)定, 第二次加鎖的時(shí)候會(huì)阻塞等待。直到第一次的鎖被釋放, 才能獲取到第二個(gè)鎖。釋放第一個(gè)鎖也是由該線程來(lái)完成, 結(jié)果這個(gè)線程已經(jīng)阻塞等待了,也就無(wú)法進(jìn)行解鎖操作。這時(shí)候就會(huì)死鎖。這樣的鎖稱為不可重入鎖。

Java中的synchronized是可重入鎖,因此不會(huì)出現(xiàn)上述問(wèn)題??芍厝腈i的內(nèi)部,包含了 "線程持有者" 和 "計(jì)數(shù)器" 兩個(gè)信息。如果某個(gè)線程進(jìn)行加鎖的時(shí)候, 發(fā)現(xiàn)鎖已經(jīng)被人占用, 占用者恰好是自己,那么仍然可以繼續(xù)獲取到鎖, 并讓計(jì)數(shù)器自增。解鎖的時(shí)候就是當(dāng)計(jì)數(shù)器遞減為0的時(shí)候, 才真正釋放鎖,這時(shí)候鎖才能被別的線程獲取到。

二、synchronized的使用

(1)修飾普通方法

鎖的 SynchronizedDemo 對(duì)象

public class SynchronizedDemo {
    public synchronized void methond() {
    }
}

(2)修飾靜態(tài)方法

鎖的 SynchronizedDemo 類的對(duì)象

public class SynchronizedDemo {
    public synchronized static void method() {
    }
}

(3)修飾代碼塊

明確指定鎖的對(duì)象

鎖當(dāng)前對(duì)象

public class SynchronizedDemo {
    public void method() {
    synchronized (this) {
        }
    }
}

鎖類對(duì)象

public class SynchronizedDemo {
    public void method() {
        synchronized (SynchronizedDemo.class) {
        }
    }
}

需要注意的是兩個(gè)線程競(jìng)爭(zhēng)同一把鎖,才會(huì)產(chǎn)生阻塞等待。兩個(gè)線程分別嘗試獲取兩把不同的鎖,不會(huì)產(chǎn)生競(jìng)爭(zhēng)。

三、synchronized的鎖機(jī)制

(1)基本特點(diǎn)

只考慮 JDK 1.8,加鎖工作過(guò)程:JVM 將 synchronized 鎖分為 無(wú)鎖、偏向鎖、輕量級(jí)鎖、重量級(jí)鎖 狀態(tài)。會(huì)根據(jù)情況,進(jìn)行依次升級(jí)。

1. 開始時(shí)是樂(lè)觀鎖, 如果鎖沖突頻繁, 就轉(zhuǎn)換為悲觀鎖.

2. 開始是輕量級(jí)鎖實(shí)現(xiàn), 如果鎖被持有的時(shí)間較長(zhǎng), 就轉(zhuǎn)換成重量級(jí)鎖.

3. 實(shí)現(xiàn)輕量級(jí)鎖的時(shí)候大概率用到的自旋鎖策略

4. 是一種不公平鎖

5. 是一種可重入鎖

6. 不是讀寫鎖

(2)加鎖工作過(guò)程

1.偏向鎖

第一個(gè)嘗試加鎖的線程,優(yōu)先進(jìn)入偏向鎖狀態(tài)。

偏向鎖不是真的 "加鎖", 只是給對(duì)象頭中做一個(gè) "偏向鎖的標(biāo)記",記錄這個(gè)鎖屬于哪個(gè)線程。如果后續(xù)沒(méi)有其他線程來(lái)競(jìng)爭(zhēng)該鎖,那么就不用進(jìn)行其他同步操作了,避免了加鎖解鎖的開銷。如果后續(xù)有其他線程來(lái)競(jìng)爭(zhēng)該鎖,因?yàn)閯偛乓呀?jīng)在鎖對(duì)象中記錄了當(dāng)前鎖屬于哪個(gè)線程了, 很容易識(shí)別當(dāng)前申請(qǐng)鎖的線程是不是之前記錄的線程, 那就取消原來(lái)的偏向鎖狀態(tài), 進(jìn)入一般的輕量級(jí)鎖狀態(tài)。偏向鎖本質(zhì)上相當(dāng)于 "延遲加鎖"。能不加鎖就不加鎖,盡量來(lái)避免不必要的加鎖開銷。但是該做的標(biāo)記還是得做的, 否則無(wú)法區(qū)分何時(shí)需要真正加鎖。

面試中會(huì)經(jīng)常問(wèn)到什么是偏向鎖。偏向鎖不是真的加鎖,而只是在鎖的對(duì)象頭中記錄一個(gè)標(biāo)記,記錄該鎖所屬的線程。如果沒(méi)有其他線程參與競(jìng)爭(zhēng)鎖,那么就不會(huì)真正執(zhí)行加鎖操作,從而降低程序開銷。一旦真的涉及到其他的線程競(jìng)爭(zhēng),再取消偏向鎖狀態(tài),進(jìn)入輕量級(jí)鎖狀態(tài)。

2.輕量級(jí)鎖

隨著其他線程進(jìn)入競(jìng)爭(zhēng),偏向鎖狀態(tài)被消除, 進(jìn)入輕量級(jí)鎖狀態(tài)(自適應(yīng)的自旋鎖)。

此處的輕量級(jí)鎖就是通過(guò) CAS 來(lái)實(shí)現(xiàn)。

通過(guò) CAS 檢查并更新一塊內(nèi)存 (比如 null => 該線程引用)。如果更新成功,則認(rèn)為加鎖成功。如果更新失敗, 則認(rèn)為鎖被占用, 繼續(xù)自旋式的等待(并不放棄 CPU)。

自旋操作是一直讓 CPU 空轉(zhuǎn), 比較浪費(fèi) CPU 資源。因此此處的自旋不會(huì)一直持續(xù)進(jìn)行, 而是達(dá)到一定的時(shí)間、重試次數(shù), 就不再自旋了。也就是所謂的 "自適應(yīng)"

3.重量級(jí)鎖

如果競(jìng)爭(zhēng)進(jìn)一步激烈, 自旋不能快速獲取到鎖狀態(tài),就會(huì)膨脹為重量級(jí)鎖。此處的重量級(jí)鎖就是指用到內(nèi)核提供的 mutex。

執(zhí)行加鎖操作, 先進(jìn)入內(nèi)核態(tài)。在內(nèi)核態(tài)判定當(dāng)前鎖是否已經(jīng)被占用。如果該鎖沒(méi)有占用, 則加鎖成功, 并切換回用戶態(tài)。如果該鎖被占用,則加鎖失敗。 此時(shí)線程進(jìn)入鎖的等待隊(duì)列、掛起。 等待被操作系統(tǒng)喚醒。經(jīng)歷了一系列的操作, 這個(gè)鎖被其他線程釋放了, 操作系統(tǒng)也想起了這個(gè)掛起的線程,于是喚醒。這個(gè)線程, 嘗試重新獲取鎖。

(3)優(yōu)化操作

1.鎖消除

編譯器+JVM 判斷鎖是否可消除。 如果可以, 就直接消除。

鎖消除:有些應(yīng)用程序的代碼中, 用到了 synchronized, 但其實(shí)沒(méi)有在多線程環(huán)境下。 (例如StringBuffer)此時(shí)每個(gè) append 的調(diào)用都會(huì)涉及加鎖、解鎖。但如果只是在單線程中執(zhí)行這個(gè)代碼, 那么這些加鎖解鎖操作是沒(méi)有必要的,,白白浪費(fèi)了一些資源開銷??梢赃M(jìn)行消除操作。
StringBuffer sb = new StringBuffer();
sb.append("a");
sb.append("b");
sb.append("c");
sb.append("d");

2.鎖粗化

一段邏輯中如果出現(xiàn)多次加鎖解鎖,編譯器 + JVM 會(huì)自動(dòng)進(jìn)行鎖的粗化。

鎖的粒度: 粗和細(xì)

實(shí)際開發(fā)過(guò)程中,使用細(xì)粒度鎖,是期望釋放鎖的時(shí)候其他線程能使用鎖。但是實(shí)際上可能并沒(méi)有其他線程來(lái)?yè)屨歼@個(gè)鎖。這種情況 JVM 就會(huì)自動(dòng)把鎖粗化, 避免頻繁申請(qǐng)釋放鎖。例如給下屬交代工作任務(wù):方式一:打電話,交代任務(wù)1, 掛電話。打電話,交代任務(wù)2,掛電話。打電話, 交代任務(wù)3, 掛電話。方式二:打電話,交代任務(wù)1,任務(wù)2,任務(wù)3,掛電話。顯然,方式二是更高效的方案。這就是鎖粗化的一個(gè)過(guò)程。

四、synchronized和volatile的區(qū)別

synchronized和volatile都是Java關(guān)鍵字,并且都是解決線程安全的方式,所以在面試的時(shí)候經(jīng)常會(huì)被放到一起問(wèn)。

兩者其實(shí)并沒(méi)有聯(lián)系。

synchronized:

1.通過(guò)加鎖、解鎖的方式,把一堆代碼綁在一起,來(lái)保證原子性。

2.通過(guò)加鎖、解鎖的方式, 來(lái)保證內(nèi)存可見性。

3.對(duì)指令重排序有一定約束。

volatile:

1.不能保證原子性。

2.保證內(nèi)存可見性。

3.禁止指令重排序。       

雖然synchronized在大多數(shù)情況下,都可以保證線程安全的。但是也不能在任何情況下都用synchronized的。synchronized是要付出一定代價(jià)的。synchronized是通過(guò)加鎖、解鎖的方式來(lái)保證的。所以,其他線程搶不到鎖的時(shí)候,線程就會(huì)阻塞。線程就會(huì)放棄CPU,放棄之后,被重新調(diào)用的時(shí)間是不確定的。當(dāng)使用synchronized就一定程度上放棄了高性能。使用volatile不會(huì)造成線程阻塞,但對(duì)性能也有一定影響,不過(guò)沒(méi)有synchronized影響大。

使用多線程是為了提高效率。使用synchronized,就代表放棄了一定效率。這兩者需要平衡。    

附:synchronized同步語(yǔ)句塊比synchronized修飾方法的優(yōu)勢(shì)

用關(guān)鍵字synchronized修飾方法簡(jiǎn)單粗暴,可以很方便的達(dá)到同步效果,但是修飾方法往往是有弊端的:一是對(duì)性能的影響,方法體通常很長(zhǎng),其他對(duì)象都要等得到鎖的那個(gè)對(duì)象釋放鎖可能是一個(gè)漫長(zhǎng)的過(guò)程。二是靈活性沒(méi)有鎖代碼塊好,synchronized修飾方法就是給方法所在的對(duì)象上鎖,而synchronized代碼塊有一個(gè)參數(shù),可以傳一個(gè)對(duì)象進(jìn)去,這個(gè)對(duì)象可以是this對(duì)象,也可以是其他對(duì)象,傳入的對(duì)象也就是需要上鎖的對(duì)象。

當(dāng)一個(gè)線程訪問(wèn)某個(gè)對(duì)象的同步代碼塊時(shí),另一個(gè)線程仍然可以訪問(wèn)這個(gè)對(duì)象中非同步代碼塊部分。也就是說(shuō):不在synchronized代碼塊中就是異步執(zhí)行,在synchronized代碼塊中就是同步執(zhí)行。

但是還有一個(gè)問(wèn)題,在一個(gè)方法里面,如果同步代碼塊在中間,那么多個(gè)線程調(diào)用的時(shí)序是怎樣的呢?答案是,在synchronized同步代碼塊之前的代碼會(huì)異步執(zhí)行,不受時(shí)間控制,但是在synchronized中的代碼以及synchronized代碼塊之后的代碼都需要排隊(duì)的。

總結(jié)

到此這篇關(guān)于Java中復(fù)雜的Synchronized關(guān)鍵字使用方法的文章就介紹到這了,更多相關(guān)Java Synchronized關(guān)鍵字內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot后端配置多個(gè)數(shù)據(jù)源、Mysql數(shù)據(jù)庫(kù)的便捷方法

    springboot后端配置多個(gè)數(shù)據(jù)源、Mysql數(shù)據(jù)庫(kù)的便捷方法

    實(shí)現(xiàn)springboot 后端配置多個(gè)數(shù)據(jù)源、Mysql數(shù)據(jù)庫(kù),只需要新建 Mapper、實(shí)體類 相應(yīng)的文件夾,將不同數(shù)據(jù)源的文件保存到對(duì)應(yīng)的文件夾下,添加綁定數(shù)據(jù)庫(kù)配置Config,就可以輕松完成
    2021-08-08
  • springboot常用的請(qǐng)求參數(shù)的接收方式

    springboot常用的請(qǐng)求參數(shù)的接收方式

    在Spring?Boot中,接收請(qǐng)求參數(shù)的方式有多種,這篇文章主要為大家整理了9個(gè)常用的方式,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-12-12
  • java編寫貪吃蛇小游戲

    java編寫貪吃蛇小游戲

    貪吃蛇是經(jīng)典手機(jī)游戲,既簡(jiǎn)單又耐玩。通過(guò)控制蛇頭方向吃蛋,使得蛇變長(zhǎng),從而獲得積分。今天我們就來(lái)用java來(lái)實(shí)現(xiàn)下貪吃蛇小游戲,有需要的小伙伴可以參考下
    2015-03-03
  • java文件/圖片的上傳與下載以及MultipartFile詳解

    java文件/圖片的上傳與下載以及MultipartFile詳解

    文章介紹了MultipartFile類的使用,包括獲取文件名、文件類型、文件大小等方法,以及如何處理多文件上傳和文件大小限制,同時(shí)提供了文件上傳和下載的示例代碼
    2025-02-02
  • springboot + mybatis + druid + 多數(shù)據(jù)源的問(wèn)題詳解

    springboot + mybatis + druid + 多數(shù)據(jù)源的問(wèn)題詳解

    這篇文章主要介紹了springboot + mybatis + druid + 多數(shù)據(jù)源的問(wèn)題詳解,示例代碼文字相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • SpringMVC?Restful風(fēng)格與中文亂碼問(wèn)題解決方案介紹

    SpringMVC?Restful風(fēng)格與中文亂碼問(wèn)題解決方案介紹

    Restful就是一個(gè)資源定位及資源操作的風(fēng)格,不是標(biāo)準(zhǔn)也不是協(xié)議,只是一種風(fēng)格,是對(duì)http協(xié)議的詮釋,下面這篇文章主要給大家介紹了關(guān)于SpringMVC對(duì)Restful風(fēng)格支持的相關(guān)資料,需要的朋友可以參考下
    2022-10-10
  • JVM垃圾回收機(jī)制和垃圾回收器詳細(xì)解說(shuō)

    JVM垃圾回收機(jī)制和垃圾回收器詳細(xì)解說(shuō)

    這篇文章主要介紹了JVM垃圾回收機(jī)制和垃圾回收器,為了讓程序員更加專注于代碼的實(shí)現(xiàn),而不用過(guò)多的考慮內(nèi)存釋放的問(wèn)題,所以在Java語(yǔ)言中,有了自動(dòng)的垃圾回收機(jī)制,也是我們常常提及的GC,需要的朋友可以參考下
    2022-07-07
  • 如何實(shí)現(xiàn)Java監(jiān)聽器詳解

    如何實(shí)現(xiàn)Java監(jiān)聽器詳解

    今天帶大家了解Java監(jiān)聽器是如何實(shí)現(xiàn)的及實(shí)現(xiàn)原理是什么,文中有非常詳細(xì)的說(shuō)明,對(duì)正在學(xué)習(xí)的小伙伴們很有幫助,需要的朋友可以參考下
    2021-06-06
  • 在Intellij Idea中使用jstl標(biāo)簽庫(kù)的方法

    在Intellij Idea中使用jstl標(biāo)簽庫(kù)的方法

    這篇文章主要介紹了在Intellij Idea中使用jstl標(biāo)簽庫(kù)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • mybatis判斷l(xiāng)ist不為空/大小的問(wèn)題

    mybatis判斷l(xiāng)ist不為空/大小的問(wèn)題

    這篇文章主要介紹了mybatis判斷l(xiāng)ist不為空/大小的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01

最新評(píng)論

西华县| 大石桥市| 中方县| 新巴尔虎右旗| 中牟县| 手游| 台南县| 连城县| 石景山区| 遂川县| 德令哈市| 潍坊市| 鹤山市| 商南县| 周宁县| 孟津县| 修武县| 如东县| 桓台县| 萨嘎县| 新竹县| 同心县| 靖边县| 青海省| 沁源县| 保定市| 新民市| 汉阴县| 正宁县| 隆回县| 正镶白旗| 鄂伦春自治旗| 鄂伦春自治旗| 晋江市| 肥西县| 唐海县| 湘乡市| 安庆市| 西昌市| 湾仔区| 万载县|