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

詳解Java中的悲觀(guān)鎖與樂(lè)觀(guān)鎖

 更新時(shí)間:2021年05月31日 09:47:41   作者:resumebb  
樂(lè)觀(guān)鎖對(duì)應(yīng)于生活中樂(lè)觀(guān)的人總是想著事情往好的方向發(fā)展,悲觀(guān)鎖對(duì)應(yīng)于生活中悲觀(guān)的人總是想著事情往壞的方向發(fā)展.這兩種人各有優(yōu)缺點(diǎn),不能不以場(chǎng)景而定說(shuō)一種人好于另外一種人,文中詳細(xì)介紹了悲觀(guān)鎖與樂(lè)觀(guān)鎖,需要的朋友可以參考下

一、悲觀(guān)鎖

悲觀(guān)鎖顧名思義是從悲觀(guān)的角度去思考問(wèn)題,解決問(wèn)題。它總是會(huì)假設(shè)當(dāng)前情況是最壞的情況,在每次去拿數(shù)據(jù)的時(shí)候,都會(huì)認(rèn)為數(shù)據(jù)會(huì)被別人改變,因此在每次進(jìn)行拿數(shù)據(jù)操作的時(shí)候都會(huì)加鎖,如此一來(lái),如果此時(shí)有別人也來(lái)拿這個(gè)數(shù)據(jù)的時(shí)候就會(huì)阻塞知道它拿到鎖。在Java中,Synchronized和ReentrantLock等獨(dú)占鎖的實(shí)現(xiàn)機(jī)制就是基于悲觀(guān)鎖思想。在數(shù)據(jù)庫(kù)中也經(jīng)常用到這種鎖機(jī)制,如行鎖,表鎖,讀寫(xiě)鎖等,都是在操作之前先上鎖,保證共享資源只能給一個(gè)操作(一個(gè)線(xiàn)程)使用。

由于悲觀(guān)鎖的頻繁加鎖,因此導(dǎo)致了一些問(wèn)題的出現(xiàn):比如在多線(xiàn)程競(jìng)爭(zhēng)下,頻繁加鎖、釋放鎖導(dǎo)致頻繁的上下文切換和調(diào)度延時(shí),一個(gè)線(xiàn)程持有鎖會(huì)導(dǎo)致其他線(xiàn)程進(jìn)入阻塞狀態(tài),從而引起性能問(wèn)題。

二、樂(lè)觀(guān)鎖

樂(lè)觀(guān)鎖從字面上看是從積極,樂(lè)觀(guān)的角度去看待問(wèn)題,因此它認(rèn)為數(shù)據(jù)一般不會(huì)產(chǎn)生沖突,因此一般不加鎖,當(dāng)數(shù)據(jù)進(jìn)行提交更新時(shí),才會(huì)真正對(duì)數(shù)據(jù)是否產(chǎn)生沖突進(jìn)行監(jiān)測(cè)。如果發(fā)生沖突,就返回給用戶(hù)錯(cuò)誤信息,由用戶(hù)來(lái)決定如何去做,主要有兩個(gè)步驟:沖突檢測(cè)和數(shù)據(jù)更新。

三、CAS

CAS(compare and set),比較和更新。CAS是樂(lè)觀(guān)鎖的技術(shù)實(shí)現(xiàn),當(dāng)多個(gè)線(xiàn)程嘗試使用CAS同時(shí)來(lái)更新同一個(gè)變量,只有一個(gè)線(xiàn)程能夠更新變量值,而其他的線(xiàn)程都會(huì)失敗,失敗的線(xiàn)程并不會(huì)被掛起,告知這次競(jìng)爭(zhēng)失敗,可以再次嘗試。

CAS操作包含三個(gè)操作數(shù):

  • 需要讀寫(xiě)的內(nèi)存位置(V)
  • 需要比較的預(yù)期原值(A)
  • 擬寫(xiě)入的新值(B)

如果內(nèi)存位置V的值與原預(yù)期值A(chǔ)相匹配,那么處理器就會(huì)自動(dòng)將該位置更新為新值B,否則處理器不做任何處理。樂(lè)觀(guān)鎖是一種思想,CAS是這種思想的一種實(shí)現(xiàn)方法。Java中對(duì)CAS支持,在jdk1.5之后新增java.util.concurrent(J.U.C)就是建立CAS基礎(chǔ)上,CAS是一種非阻塞的實(shí)現(xiàn),例如:Atomic

四、AtomicXXX

在Java中,提供了一些原子化的操作類(lèi)型,如下操作

 private volatile int value;
 
public final int get() {
        return value;
    }

讀取的值,value是聲明為volatile的,就可以保證在沒(méi)有鎖的情況下,線(xiàn)程可見(jiàn)性

在涉及到數(shù)據(jù)變更,以incrementAndGet實(shí)例:++i操作

public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

采用的CAS的操作,每次讀取內(nèi)存中的數(shù)據(jù),讓后將數(shù)據(jù)+1的結(jié)果進(jìn)行CAS操作,如果成功就返回結(jié)果,負(fù)責(zé)重試指導(dǎo)成功為止,這里調(diào)用compareAndSet是CAS所依賴(lài)的JNI的實(shí)現(xiàn)的樂(lè)觀(guān)鎖 。

public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

Atomic就是volatile的使用場(chǎng)景,也是CAS的使用場(chǎng)景。

五、CAS中的ABA問(wèn)題

CAS使用起來(lái)能夠提高性能,但會(huì)引起ABA的問(wèn)題

假如如下事件序列:

1、線(xiàn)程1從內(nèi)次位置V來(lái)獲取值A(chǔ)

2、線(xiàn)程2從內(nèi)存位置V獲取A

3、線(xiàn)程2進(jìn)行一些操作,將B寫(xiě)入到V

4、線(xiàn)程2將A寫(xiě)入位置V

5、線(xiàn)程1進(jìn)行CAS操作,發(fā)現(xiàn)位置V的值任然為A,操作成功了

6、線(xiàn)程1盡管CAS操作成功了,該過(guò)程有可能出現(xiàn)問(wèn)題,對(duì)于線(xiàn)程1,線(xiàn)程2做的處理就可能丟失了

舉例說(shuō)明:一個(gè)鏈表ABA的例子

1、現(xiàn)有一個(gè)用單向鏈表實(shí)現(xiàn)的堆棧,棧頂為A。這時(shí)線(xiàn)程T1已經(jīng)知道A.next為B,然后希望用CAS將棧頂替換為B:

1head.compareAndSet(A,B);

2、在T1執(zhí)行上面這條指令之前,線(xiàn)程T2介入,將A、B出棧,再依次入棧D、C、A,而對(duì)象B此時(shí)處于游離狀態(tài)。

3、此時(shí)輪到線(xiàn)程T1執(zhí)行CAS操作,檢測(cè)發(fā)現(xiàn)棧頂仍為A,所以CAS成功,棧頂變?yōu)锽。但實(shí)際上B.next為null,此時(shí)堆棧中只有B一個(gè)元素,C和D組成的鏈表不再存在于堆棧中,C、D被丟掉了。

image.png

六、ABA問(wèn)題解決方案

ABA問(wèn)題解決思路就是使用版本號(hào),在變量前面追加版本號(hào),每次對(duì)變量你進(jìn)行更新的時(shí)候?qū)Π姹具M(jìn)行加1,對(duì)于A(yíng)->B->A 就會(huì)變成1A ->2B->3A

七、使用CAS會(huì)引起的問(wèn)題

1.ABA問(wèn)題

ABA問(wèn)題可以使用版本號(hào)解決

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

 自旋CAS如果長(zhǎng)時(shí)間不成功,CPU帶來(lái)非常大的執(zhí)行開(kāi)銷(xiāo),需要考慮長(zhǎng)時(shí)間循環(huán)問(wèn)題,給每個(gè)線(xiàn)程循環(huán)給定循環(huán)次數(shù)閾值,讓當(dāng)前線(xiàn)程釋放CPU的使用權(quán),進(jìn)入阻塞中

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

八、Synchronized鎖優(yōu)化

JDK1.5之前, Synchronized稱(chēng)之為“重量級(jí)鎖”,對(duì)該做了各種所有,分別為偏向鎖、輕量級(jí)鎖、重量級(jí)鎖

Java對(duì)象內(nèi)存布局:

說(shuō)到 synchronized 加鎖原理與Java對(duì)象在內(nèi)存中的布局有很大關(guān)系, Java 對(duì)象內(nèi)存布局如下:

圖片.png

如上圖所示,在創(chuàng)建一個(gè)對(duì)象后,在 JVM 虛擬機(jī)( HotSpot )中,對(duì)象在 Java 內(nèi)存中的存儲(chǔ)布局 可分為三塊:

對(duì)象頭區(qū)域

存放鎖信息,對(duì)象年齡等信息

實(shí)例數(shù)據(jù)區(qū)域

此處存儲(chǔ)的是對(duì)象真正有效的信息,比如對(duì)象中所有字段的內(nèi)容

對(duì)齊填充區(qū)域

JVM 的實(shí)現(xiàn) HostSpot 規(guī)定對(duì)象的起始地址必須是 8 字節(jié)的整數(shù)倍,換句話(huà)來(lái)說(shuō),現(xiàn)在 64 位的 OS 往外讀取數(shù)據(jù)的時(shí)候一次性讀取 64bit 整數(shù)倍的數(shù)據(jù),也就是 8 個(gè)字節(jié),所以 HotSpot 為了高效讀取對(duì)象,就做了"對(duì)齊",如果一個(gè)對(duì)象實(shí)際占的內(nèi)存大小不是 8byte 的整數(shù)倍時(shí),就"補(bǔ)位"到 8byte 的整數(shù)倍。所以對(duì)齊填充區(qū)域的大小不是固定的。

synchronized用的鎖是存在Java對(duì)象頭里的,如果對(duì)象是數(shù)組類(lèi)型,則虛擬機(jī)用3個(gè)字寬(Word)存儲(chǔ)對(duì)象頭,如果對(duì)象是非數(shù)組類(lèi)型,則用2字寬存儲(chǔ)對(duì)象頭。在32位虛擬機(jī)中,1字寬等于4字節(jié),即32bit,如下圖:

圖片.png

Java對(duì)象頭里的Mark Word里默認(rèn)存儲(chǔ)對(duì)象的HashCode、分代年齡和鎖標(biāo)記位。32位JVM的Mark Word的默認(rèn)存儲(chǔ)結(jié)構(gòu)如下圖所示:

圖片.png

在Java SE 1.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í)的策略,目的是為了提高獲得鎖和釋放鎖的效率。

圖片.png

九、偏向鎖

偏向鎖的操作根本沒(méi)有去找操作系統(tǒng), 每個(gè)對(duì)象都有對(duì)象頭,看看這個(gè)account對(duì)象的所謂“對(duì)象頭”,其中有個(gè)叫做Mark Word:里邊有幾個(gè)標(biāo)識(shí)位,還有其他數(shù)據(jù)。

圖片.png

JVM使用CAS操作把線(xiàn)程ID記錄到了這個(gè)Mark Word當(dāng)中,修改了標(biāo)識(shí)位,當(dāng)前線(xiàn)程就擁有這把鎖了

圖片.png

可以看出:JVM不用和操作系統(tǒng)協(xié)商設(shè)置Mutex,它只記錄下線(xiàn)程ID,就表示當(dāng)前線(xiàn)程擁有這把鎖了,不用操作系統(tǒng)介入

這時(shí)線(xiàn)程獲得了鎖,可以執(zhí)行synchronized修飾的代碼塊。

當(dāng)線(xiàn)程再次執(zhí)行到這個(gè)synchronized的時(shí)候,JVM通過(guò)鎖對(duì)象account的Mark Word判斷:“當(dāng)前線(xiàn)程ID還在,還持有著這個(gè)對(duì)象的鎖,就可以繼續(xù)進(jìn)入臨界區(qū)執(zhí)行

這就是偏向鎖,在沒(méi)有別的線(xiàn)程競(jìng)爭(zhēng)的時(shí)候,一直偏向當(dāng)前線(xiàn)程,當(dāng)前線(xiàn)程可以一直執(zhí)行

十、輕量級(jí)鎖

繼續(xù)沿著偏向鎖思路研究

另一個(gè)線(xiàn)程0x3704也要進(jìn)入這個(gè)代碼塊執(zhí)行,但是鎖對(duì)象account 保存的是當(dāng)前線(xiàn)程ID,他是沒(méi)法進(jìn)入臨界區(qū)的。

這時(shí)也不需要和操作系統(tǒng)交流,JVM可以對(duì)偏向鎖升級(jí)一下,變成一個(gè)輕量級(jí)的鎖。

JVM把鎖對(duì)象account恢復(fù)成無(wú)鎖狀態(tài),在當(dāng)前兩線(xiàn)程的棧幀中各自分配了一個(gè)空間,叫做Lock Record,把鎖對(duì)象account的Mark Word在倆線(xiàn)程的棧幀中各自復(fù)制了一份,叫做Displaced Mark Word

然后當(dāng)前線(xiàn)程的Lock Record的地址使用CAS放到了Mark Word當(dāng)中,并且把鎖標(biāo)志位改為00, 這意味著當(dāng)前線(xiàn)程也已經(jīng)獲得了這個(gè)輕量級(jí)的鎖了,可以繼續(xù)進(jìn)入臨界區(qū)執(zhí)行。

 圖片.png

0x3704線(xiàn)程沒(méi)有獲得鎖,但不阻塞,JVM讓他自旋幾次,等待一會(huì)兒。等當(dāng)前退出臨界區(qū),釋放鎖的時(shí)候,需要把這個(gè)Displaced markd word 使用CAS復(fù)制回去。接下來(lái)他就可以加鎖了。

兩線(xiàn)程交替著進(jìn)入臨界區(qū),執(zhí)行這段代碼,相安無(wú)事,很少出現(xiàn)真正的競(jìng)爭(zhēng)。

即使是出現(xiàn)了競(jìng)爭(zhēng),想獲得鎖的線(xiàn)程只要自旋幾次,等待一會(huì)兒,鎖就可能釋放了。

很明顯,如果沒(méi)有競(jìng)爭(zhēng)或者輕度的競(jìng)爭(zhēng),輕量級(jí)鎖僅僅使用CAS操作和Lock record就避免了重量級(jí)互斥鎖的開(kāi)銷(xiāo)

十一、重量級(jí)鎖

再次分析:輕量級(jí)鎖運(yùn)行時(shí),一線(xiàn)程0x3704 正在持有鎖。另一線(xiàn)程自旋了好多次,0x3704還是沒(méi)釋放鎖。 這時(shí)候JVM考慮自旋次數(shù)太多了浪費(fèi)CPU。接則升級(jí)為重量級(jí)鎖!

重量級(jí)鎖需要操作系統(tǒng)的介入,依賴(lài)操作系統(tǒng)底層的Mutex Lock。

JVM創(chuàng)建了一個(gè)monitor 對(duì)象,把這個(gè)對(duì)象的地址更新到了Mark word當(dāng)中。

圖片.png

在持有鎖運(yùn)行,而另一線(xiàn)程則切換進(jìn)程狀態(tài)至:阻塞

到此這篇關(guān)于詳解Java中的悲觀(guān)鎖與樂(lè)觀(guān)鎖的文章就介紹到這了,更多相關(guān)悲觀(guān)鎖與樂(lè)觀(guān)鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Mybatis中的高級(jí)映射一對(duì)一、一對(duì)多、多對(duì)多

    Mybatis中的高級(jí)映射一對(duì)一、一對(duì)多、多對(duì)多

    這篇文章主要介紹了Mybatis中的高級(jí)映射一對(duì)一、一對(duì)多、多對(duì)多的相關(guān)資料,需要的朋友可以參考下
    2016-08-08
  • JAVA 格式化日期、時(shí)間的方法

    JAVA 格式化日期、時(shí)間的方法

    這篇文章主要介紹了JAVA 格式化日期、時(shí)間的方法,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • 詳解spring如何使用注解開(kāi)發(fā)

    詳解spring如何使用注解開(kāi)發(fā)

    今天給大家整理了spring如何使用注解開(kāi)發(fā)這篇文章,文中有非常詳細(xì)的介紹及代碼示例,對(duì)正在學(xué)習(xí)的小伙伴們很有幫助,需要的朋友可以參考下
    2021-05-05
  • Spring Boot之內(nèi)嵌tomcat版本升級(jí)操作示例

    Spring Boot之內(nèi)嵌tomcat版本升級(jí)操作示例

    這篇文章主要為大家介紹了Spring Boot之內(nèi)嵌tomcat版本升級(jí)操作示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Java編程中的構(gòu)造函數(shù)詳細(xì)介紹

    Java編程中的構(gòu)造函數(shù)詳細(xì)介紹

    這篇文章主要介紹了Java編程中的構(gòu)造函數(shù)詳細(xì)介紹,介紹了其概念,格式,與其他函數(shù)的區(qū)別,作用等相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • 淺談java 中文件的讀取File、以及相對(duì)路徑的問(wèn)題

    淺談java 中文件的讀取File、以及相對(duì)路徑的問(wèn)題

    今天小編就為大家分享一篇淺談java 中文件的讀取File、以及相對(duì)路徑的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • 基于CXF搭建webService的實(shí)例講解

    基于CXF搭建webService的實(shí)例講解

    下面小編就為大家?guī)?lái)一篇基于CXF搭建webService的實(shí)例講解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • Vert-x-通過(guò)異步的方式使用JDBC連接SQL

    Vert-x-通過(guò)異步的方式使用JDBC連接SQL

    在這篇文章中,我們將會(huì)看到怎樣在vert.x應(yīng)用中使用HSQL,當(dāng)然也可以使用任意JDBC,以及使用vertx-jdbc-client提供的異步的API,這篇文章的代碼在github
    2016-01-01
  • SpringBoot2.x的依賴(lài)管理配置

    SpringBoot2.x的依賴(lài)管理配置

    這篇文章主要介紹了SpringBoot2.x的依賴(lài)管理配置,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • 詳解如何使用tldb數(shù)據(jù)庫(kù)的java客戶(hù)端

    詳解如何使用tldb數(shù)據(jù)庫(kù)的java客戶(hù)端

    這篇文章主要為大家介紹了如何使用tldb數(shù)據(jù)庫(kù)的java客戶(hù)端過(guò)程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09

最新評(píng)論