欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JAVAsynchronized原理詳解

 更新時(shí)間:2021年08月20日 08:50:23   作者:須佐能乎!  
這篇文章主要介紹了Java中synchronized實(shí)現(xiàn)原理詳解,涉及synchronized實(shí)現(xiàn)同步的基礎(chǔ),Java對(duì)象頭,Monitor,Mark Word,鎖優(yōu)化,自旋鎖等相關(guān)內(nèi)容,具有一定借鑒價(jià)值,需要的朋友可以參考下

1、synchronized的作用

為了避免臨界區(qū)的競(jìng)態(tài)條件發(fā)生,有多種手段可以達(dá)到目的。

  • 阻塞式的解決方案:synchronized,Lock
  • 非阻塞式的解決方案:原子變量

synchronized,即俗稱(chēng)的【對(duì)象鎖】,它采用互斥的方式讓同一時(shí)刻至多只有一個(gè)線程能持有【對(duì)象鎖】,其它線程再想獲取這個(gè)【對(duì)象鎖】時(shí)就會(huì)阻塞住。這樣就能保證擁有鎖的線程可以安全的執(zhí)行臨界區(qū)內(nèi)的代碼,不用擔(dān)心線程上下文切換。

synchronized的三個(gè)作用

  1. 原子性:確保線程互斥的訪問(wèn)同步代碼
  2. 可見(jiàn)性:保證共享變量的修改能夠及時(shí)可見(jiàn)
  3. 有序性:有效解決重排序問(wèn)題

2、synchronized的語(yǔ)法

class Test1{
    public synchronized void test() {
    }
}
//等價(jià)于
class Test1{
    public void test() {
        //鎖的是當(dāng)前對(duì)象
        synchronized(this) {
        }
    }
}
class Test2{
    public synchronized static void test() {
    }
}
//等價(jià)于
class Test2{
    public static void test() {
        //鎖的是類(lèi)對(duì)象,類(lèi)對(duì)象只有一個(gè)
        synchronized(Test2.class) {
        }
    }
}

3、Monitor原理

Monitor 被翻譯為監(jiān)視器或管程

每個(gè) Java 對(duì)象都可以關(guān)聯(lián)一個(gè) Monitor 對(duì)象,如果使用 synchronized 給對(duì)象上鎖(重量級(jí))之后,該對(duì)象頭的 Mark Word 中就被設(shè)置指向 Monitor 對(duì)象的指針

Monitor 結(jié)構(gòu)如下

  • 剛開(kāi)始 Monitor 中 Owner 為 null
  • 當(dāng) Thread-2 執(zhí)行 synchronized(obj) 就會(huì)將 Monitor 的所有者 Owner 置為 Thread-2,Monitor中只能有一個(gè) Owner
  • 在 Thread-2 上鎖的過(guò)程中,如果 Thread-3,Thread-4,Thread-5 也來(lái)執(zhí)行 synchronized(obj),就會(huì)進(jìn)入EntryList BLOCKED
  • Thread-2 執(zhí)行完同步代碼塊的內(nèi)容,然后喚醒 EntryList 中等待的線程來(lái)競(jìng)爭(zhēng)鎖,競(jìng)爭(zhēng)的時(shí)是非公平的
  • 圖中 WaitSet 中的 Thread-0,Thread-1 是之前獲得過(guò)鎖,但條件不滿足進(jìn)入 WAITING 狀態(tài)的線程

注意:不加 synchronized 的對(duì)象不會(huì)關(guān)聯(lián)監(jiān)視器

4、synchronized的原理

通過(guò)對(duì)Java代碼進(jìn)行反編譯可知,Synchronized的語(yǔ)義底層是通過(guò)一個(gè)monitor的對(duì)象來(lái)完成,
其實(shí)wait/notify等方法也依賴(lài)于monitor對(duì)象,這就是為什么只有在同步的塊或者方法中才能調(diào)用wait/notify等方法,否則會(huì)拋出java.lang.IllegalMonitorStateException的異常的原因。

從JDK5引入了現(xiàn)代操作系統(tǒng)新增加的CAS原子操作( JDK5中并沒(méi)有對(duì)synchronized關(guān)鍵字做優(yōu)化,而是體現(xiàn)在J.U.C中,所以在該版本concurrent包有更好的性能 ),從JDK6開(kāi)始,就對(duì)synchronized的實(shí)現(xiàn)機(jī)制進(jìn)行了較大調(diào)整,包括使用JDK5引進(jìn)的CAS自旋之外,還增加了自適應(yīng)的CAS自旋、鎖消除、鎖粗化、偏向鎖、輕量級(jí)鎖這些優(yōu)化策略。由于此關(guān)鍵字的優(yōu)化使得性能極大提高.

鎖主要存在四種狀態(tài),依次是:無(wú)鎖狀態(tài)、偏向鎖狀態(tài)、輕量級(jí)鎖狀態(tài)、重量級(jí)鎖狀態(tài),鎖可以從偏向鎖升級(jí)到輕量級(jí)鎖,再升級(jí)的重量級(jí)鎖。但是鎖的升級(jí)是單向的,也就是說(shuō)只能從低到高升級(jí),不會(huì)出現(xiàn)鎖的降級(jí)。

在 JDK 1.6 中默認(rèn)是開(kāi)啟偏向鎖和輕量級(jí)鎖的,可以通過(guò)-XX:-UseBiasedLocking來(lái)禁用偏向鎖。

4.1偏向鎖

Java 6 中引入了偏向鎖來(lái)做進(jìn)一步優(yōu)化:只有第一次使用 CAS 將線程 ID 設(shè)置到對(duì)象的 Mark Word 頭,之后發(fā)現(xiàn) 這個(gè)線程 ID 是自己的就表示沒(méi)有競(jìng)爭(zhēng),不用重新 CAS。以后只要不發(fā)生競(jìng)爭(zhēng),這個(gè)對(duì)象就歸該線程所有。

 

調(diào)用了對(duì)象的 hashCode,但偏向鎖的對(duì)象 MarkWord 中存儲(chǔ)的是線程 id,如果調(diào)用 hashCode 會(huì)導(dǎo)致偏向鎖被撤銷(xiāo)

  • 輕量級(jí)鎖會(huì)在鎖記錄中記錄 hashCode
  • 重量級(jí)鎖會(huì)在 Monitor 中記錄 hashCode

4.2輕量級(jí)鎖

輕量級(jí)鎖的使用場(chǎng)景:如果一個(gè)對(duì)象雖然有多線程要加鎖,但加鎖的時(shí)間是錯(cuò)開(kāi)的(也就是沒(méi)有競(jìng)爭(zhēng)),那么可以使用輕量級(jí)鎖來(lái)優(yōu)化。
輕量級(jí)鎖對(duì)使用者是透明的,即語(yǔ)法仍然是 synchronized。

引入輕量級(jí)鎖的主要目的是 在沒(méi)有多線程競(jìng)爭(zhēng)的前提下,減少傳統(tǒng)的重量級(jí)鎖使用操作系統(tǒng)互斥量產(chǎn)生的性能消耗。當(dāng)關(guān)閉偏向鎖功能或者多個(gè)線程競(jìng)爭(zhēng)偏向鎖導(dǎo)致偏向鎖升級(jí)為輕量級(jí)鎖,則會(huì)嘗試獲取輕量級(jí)鎖。

4.3鎖膨脹

如果在嘗試加輕量級(jí)鎖的過(guò)程中,CAS 操作無(wú)法成功,這時(shí)一種情況就是有其它線程為此對(duì)象加上了輕量級(jí)鎖(有 競(jìng)爭(zhēng)),這時(shí)需要進(jìn)行鎖膨脹,將輕量級(jí)鎖變?yōu)橹亓考?jí)鎖。

4.4重量級(jí)鎖

Synchronized是通過(guò)對(duì)象內(nèi)部的一個(gè)叫做 監(jiān)視器鎖(Monitor)來(lái)實(shí)現(xiàn)的。但是監(jiān)視器鎖本質(zhì)又是依賴(lài)于底層的操作系統(tǒng)的Mutex Lock來(lái)實(shí)現(xiàn)的。而操作系統(tǒng)實(shí)現(xiàn)線程之間的切換這就需要從用戶(hù)態(tài)轉(zhuǎn)換到核心態(tài),這個(gè)成本非常高,狀態(tài)之間的轉(zhuǎn)換需要相對(duì)比較長(zhǎng)的時(shí)間,這就是為什么Synchronized效率低的原因。因此,這種依賴(lài)于操作系統(tǒng)Mutex Lock所實(shí)現(xiàn)的鎖我們稱(chēng)之為 “重量級(jí)鎖”。

4.5自旋鎖

線程的阻塞和喚醒需要CPU從用戶(hù)態(tài)轉(zhuǎn)為核心態(tài),頻繁的阻塞和喚醒對(duì)CPU來(lái)說(shuō)是一件負(fù)擔(dān)很重的工作,勢(shì)必會(huì)給系統(tǒng)的并發(fā)性能帶來(lái)很大的壓力。同時(shí)我們發(fā)現(xiàn)在許多應(yīng)用上面,對(duì)象鎖的鎖狀態(tài)只會(huì)持續(xù)很短一段時(shí)間,為了這一段很短的時(shí)間頻繁地阻塞和喚醒線程是非常不值得的。

所以引入自旋鎖,何謂自旋鎖?

所謂自旋鎖,就是指當(dāng)一個(gè)線程嘗試獲取某個(gè)鎖時(shí),如果該鎖已被其他線程占用,就一直循環(huán)檢測(cè)鎖是否被釋放,而不是進(jìn)入線程掛起或睡眠狀態(tài)。

4.6鎖消除

消除鎖是虛擬機(jī)另外一種鎖的優(yōu)化,這種優(yōu)化更徹底,Java虛擬機(jī)在JIT編譯時(shí)(可以簡(jiǎn)單理解為當(dāng)某段代碼即將第一次被執(zhí)行時(shí)進(jìn)行編譯,又稱(chēng)即時(shí)編譯),通過(guò)對(duì)運(yùn)行上下文的掃描,去除不可能存在共享資源競(jìng)爭(zhēng)的鎖,通過(guò)這種方式消除沒(méi)有必要的鎖,可以節(jié)省毫無(wú)意義的請(qǐng)求鎖時(shí)間,如下StringBuffer的append是一個(gè)同步方法,但我們將StringBuffer作為一個(gè)局部變量使用,并且不會(huì)被其他線程所使用,因此StringBuffer不可能存在共享資源競(jìng)爭(zhēng)的情景,JVM會(huì)自動(dòng)將其鎖消除。

4.7鎖粗化

在使用同步鎖的時(shí)候,需要讓同步塊的作用范圍盡可能小—僅在共享數(shù)據(jù)的實(shí)際作用域中才進(jìn)行同步,這樣做的目的是 為了使需要同步的操作數(shù)量盡可能縮小,如果存在鎖競(jìng)爭(zhēng),那么等待鎖的線程也能盡快拿到鎖。

在大多數(shù)的情況下,上述觀點(diǎn)是正確的。但是如果一系列的連續(xù)加鎖解鎖操作,可能會(huì)導(dǎo)致不必要的性能損耗,所以引入鎖粗話的概念。

鎖粗話概念比較好理解,就是將多個(gè)連續(xù)的加鎖、解鎖操作連接在一起,擴(kuò)展成一個(gè)范圍更大的鎖

5、鎖升級(jí)過(guò)程

各種鎖并不是相互代替的,而是在不同場(chǎng)景下的不同選擇,絕對(duì)不是說(shuō)重量級(jí)鎖就是不合適的。每種鎖是只能升級(jí),不能降級(jí),即由偏向鎖->輕量級(jí)鎖->重量級(jí)鎖,而這個(gè)過(guò)程就是開(kāi)銷(xiāo)逐漸加大的過(guò)程。

如果是單線程使用,那偏向鎖毫無(wú)疑問(wèn)代價(jià)最小,并且它就能解決問(wèn)題,連CAS都不用做,僅僅在內(nèi)存中比較下對(duì)象頭就可以了;

如果出現(xiàn)了其他線程競(jìng)爭(zhēng),則偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖;

如果其他線程通過(guò)一定次數(shù)的CAS嘗試沒(méi)有成功,則進(jìn)入重量級(jí)鎖;

總結(jié)

本篇文章就到這里了,希望能給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • Java面向?qū)ο蠡A(chǔ),類(lèi),變量,方法

    Java面向?qū)ο蠡A(chǔ),類(lèi),變量,方法

    這篇文章主要介紹了Java面向?qū)ο蠡A(chǔ),類(lèi),變量,方法,需要的朋友可以參考下
    2020-10-10
  • 快速解決idea打開(kāi)某個(gè)項(xiàng)目卡住的問(wèn)題

    快速解決idea打開(kāi)某個(gè)項(xiàng)目卡住的問(wèn)題

    這篇文章主要介紹了解決idea打開(kāi)某個(gè)項(xiàng)目卡住的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • MybatisPlus處理四種表與實(shí)體的映射及id自增策略分析

    MybatisPlus處理四種表與實(shí)體的映射及id自增策略分析

    在最近的工作中,碰到一個(gè)比較復(fù)雜的返回結(jié)果,發(fā)現(xiàn)簡(jiǎn)單映射已經(jīng)解決不了這個(gè)問(wèn)題了,只好去求助百度,學(xué)習(xí)mybatis表與實(shí)體的映射應(yīng)該怎么寫(xiě),將學(xué)習(xí)筆記結(jié)合工作碰到的問(wèn)題寫(xiě)下本文,供自身查漏補(bǔ)缺,同時(shí)已被不時(shí)之需
    2022-10-10
  • Java多線程中的Callable和Future詳解

    Java多線程中的Callable和Future詳解

    這篇文章主要介紹了Java多線程中的Callable和Future詳解,創(chuàng)建線程的兩種方式,一種是直接繼承Thread,另外一種就是實(shí)現(xiàn)Runnable接口,本文提供了部分代碼,需要的朋友可以參考下
    2023-08-08
  • springboot+springsecurity如何實(shí)現(xiàn)動(dòng)態(tài)url細(xì)粒度權(quán)限認(rèn)證

    springboot+springsecurity如何實(shí)現(xiàn)動(dòng)態(tài)url細(xì)粒度權(quán)限認(rèn)證

    這篇文章主要介紹了springboot+springsecurity如何實(shí)現(xiàn)動(dòng)態(tài)url細(xì)粒度權(quán)限認(rèn)證的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java中的static--靜態(tài)變量你了解嗎

    Java中的static--靜態(tài)變量你了解嗎

    Java 中被 static 修飾的成員稱(chēng)為靜態(tài)成員或類(lèi)成員。它屬于整個(gè)類(lèi)所有,而不是某個(gè)對(duì)象所有,即被類(lèi)的所有對(duì)象所共享。靜態(tài)成員可以使用類(lèi)名直接訪問(wèn),也可以使用對(duì)象名進(jìn)行訪問(wèn),.下面我們來(lái)詳細(xì)了解一下吧
    2021-09-09
  • 詳解Spring連接數(shù)據(jù)庫(kù)的幾種常用的方式

    詳解Spring連接數(shù)據(jù)庫(kù)的幾種常用的方式

    本篇文章主要介紹了Spring連接數(shù)據(jù)庫(kù)的幾種常用的方式,具有一定的參考價(jià)值,有需要的可以了解一下。
    2016-12-12
  • Java常用字符串方法小結(jié)

    Java常用字符串方法小結(jié)

    字符串變量是Java與C語(yǔ)言的一大不同之處。Java之中的 String 類(lèi)和 Stringbuffer 類(lèi)提供了大量的對(duì)字符串操作的方法。String 類(lèi)適合處理較小的字符串,而Stringbuffer類(lèi)適合處理大量字符串
    2017-04-04
  • java爬蟲(chóng)Gecco工具抓取新聞實(shí)例

    java爬蟲(chóng)Gecco工具抓取新聞實(shí)例

    本篇文章主要介紹了JAVA 爬蟲(chóng)Gecco工具抓取新聞實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2016-10-10
  • Java多線程+鎖機(jī)制實(shí)現(xiàn)簡(jiǎn)單模擬搶票的項(xiàng)目實(shí)踐

    Java多線程+鎖機(jī)制實(shí)現(xiàn)簡(jiǎn)單模擬搶票的項(xiàng)目實(shí)踐

    鎖是一種同步機(jī)制,用于控制對(duì)共享資源的訪問(wèn),在線程獲取到鎖對(duì)象后,可以執(zhí)行搶票操作,本文主要介紹了Java多線程+鎖機(jī)制實(shí)現(xiàn)簡(jiǎn)單模擬搶票的項(xiàng)目實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02

最新評(píng)論