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

詳解java并發(fā)編程(2) --Synchronized與Volatile區(qū)別

 更新時間:2019年04月03日 15:48:41   作者:正先生  
這篇文章主要介紹了Synchronized與Volatile區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

1 Synchronized

在多線程并發(fā)中synchronized一直是元老級別的角色。利用synchronized來實(shí)現(xiàn)同步具體有一下三種表現(xiàn)形式:

  1. 對于普通的同步方法,鎖是當(dāng)前實(shí)例對象。
  2. 對于靜態(tài)同步方法,鎖是當(dāng)前類的class對象。
  3. 對于同步方法塊,鎖是synchronized括號里配置的對象。

當(dāng)一個代碼,方法或者類被synchronized修飾以后。當(dāng)一個線程試圖訪問同步代碼塊的時候,它首先必須得到鎖,退出或拋出異常的時候必須釋放鎖。那么這樣做有什么好處呢?

它主要確保多個線程在同一時刻,只能有一個線程處于方法或者同步塊中,它保證了線程對變量的可見性和排他性。

1.1 如何實(shí)現(xiàn)排他性

如下圖所示,一個普通的方法會有一個左右擺動的開關(guān),可以連接到任意一個線程,如果該方法代碼不是原子性的,可能會出現(xiàn)一個線程并沒有將方法代碼執(zhí)行完畢就鏈接到另一個線程中去。而被synchronized修飾的方法,鏈接到一個線程后,除非這個線程將方法執(zhí)行完畢或者拋出異常,開關(guān)才會鏈接至別的線程。就這樣將一個并行的操作變了穿行操作。(同一時間保證只有一個線程在執(zhí)行方法代碼)

 int i = 1;
  public synchronized void increment(){
    i++;
  }

在前面并發(fā)基礎(chǔ)及鎖的原理中我們介紹過i++并不是原子操作,所有當(dāng)多個線程同時操作i++的時候可能會出現(xiàn)多線程并發(fā)問題。而上訴代碼塊中i++是在synchronized修飾的方法中。其中一個線程進(jìn)入該方法首先獲得當(dāng)前實(shí)例對象的鎖,當(dāng)另一個線程試圖執(zhí)行該方法的時候,由于前一個線程并沒有執(zhí)行完畢釋放掉鎖,所以該線程掛起等待鎖的釋放。

通過加鎖的方式我們實(shí)現(xiàn)了將i++非原子操作的方法變成了原子操作的方法。從而實(shí)現(xiàn)了排他性。

1.2 如何實(shí)現(xiàn)可見性

因?yàn)樵趈ava內(nèi)存模型中規(guī)定:在執(zhí)行被synchronized修飾的代碼時,線程首先獲取鎖→清空工作內(nèi)存→在主內(nèi)存中拷貝最新變量的副本到工作內(nèi)存→執(zhí)行完代碼→將工作內(nèi)存中更改后的共享變量的值刷新到主內(nèi)存中→釋放互斥鎖。

這里有一個細(xì)節(jié)需要注意: 當(dāng)一個線程A將最新的共享變量刷新到主內(nèi)存的時候,會導(dǎo)致緩存在其他線程B的工作內(nèi)存的這個共享變量失效。
當(dāng)線程B下一次去訪問這個變量的時候,會發(fā)現(xiàn),工作緩存的這個變量已經(jīng)失效。會強(qiáng)制從主內(nèi)存中重新讀取這個共享變量

2 Volatile

當(dāng)聲明共享變量為volatile后,對這個變量的讀/寫將會很特別。volatile可以說是java虛擬機(jī)提供的最輕量級的同步機(jī)制。他只能能只能保證變量的可見性與讀/寫的原子性。要理解volatile確實(shí)是不容易的,接下來我們進(jìn)入深入的分析!

2.1 volatile的特性

下面有兩個示例代碼:

public class VolatileTest1 {
  volatile long a = 0L;          //使用volatile聲明64位的long型變量

  public void set(long b) {      
    a = b;               //單個volatile變量的寫
  }

  public void increment() {      
    a++;                //復(fù)合(多個)volatile變量的讀/寫
  }

  public long get() {
    return a;              //的那個volatile變量的讀
  }
}
public class VolatileTest2 {
  long a = 0L;                //64位的普通long型變量

  public synchronized void set(long b) {   //單個普通變量的寫使用同步鎖
    a = b;
  }

  public void increment() {          //普通方法調(diào)用
    long tmp = get();            //調(diào)用以同步的讀方法
    tmp += 1;                //普通的寫操作
    set(tmp);                //調(diào)用以同步的寫方法
  }

  public synchronized long get() {      //單個普通變量的讀使用同步鎖
    return a;            
  }
}

上述兩個示例代碼所帶來的的執(zhí)行效果是相同的。

可以看到被volatile修飾的變量讀與寫操作是原子性的。如前面所述,被Synchronized修飾的變量每次寫操作完成后,會強(qiáng)制將工作內(nèi)存中緩存的共享變量強(qiáng)制刷新到主內(nèi)存中。所以保證了volatile修飾變量的可見性。

從上述示例代碼中我們也能看出,即便讀與寫是原子性,但是依舊不能保證 a++;是原子操作。這也是很多人對volatile字段理解困難的原因所在。

簡而言之,volatile變量自身具有下列特征。

  1. 可見性:對一個volatile變量的讀,總是能看到(任意線程)對這個volatile變量最后的寫入。
  2. 原子性:對任意單個volat變量的讀 / 寫具有原子性,但類似volatile++這種復(fù)合操作不具有原子性。

在這里樓主插一個之前遇到的面試題:請問對于double和long類型的讀寫是原子性的嗎?double和long類型是64位的,在一些32位的處理器上,可能會把一個64位的long/double型變量的寫操作才分為兩個32位的寫操作來執(zhí)行。座椅此時對這個64位變量的寫操作將不具有原子性。但是如果被volatile修飾的話,寫64位的double和long的操作依舊是原子操作。

2.2 volatile的禁止重排序

除了前面內(nèi)存可見性中講到的volatile關(guān)鍵字可以保證變量修改的可見性之外,還有另一個重要的作用:在JDK1.5之后,可以使用volatile變量禁止指令重排序。

volatile關(guān)鍵字通過提供“內(nèi)存屏障”的方式來防止指令被重排序,為了實(shí)現(xiàn)volatile的內(nèi)存語義,編譯器在生成字節(jié)碼時,會在指令序列中插入內(nèi)存屏障來禁止特定類型的處理器重排序。

對于編譯器來說,發(fā)現(xiàn)一個最優(yōu)布置來最小化插入屏障的總數(shù)幾乎不可能,為此,Java內(nèi)存模型采取保守策略。下面是基于保守策略的JMM內(nèi)存屏障插入策略:

  1. 在每個volatile寫操作的前面插入一個StoreStore屏障。
  2. 在每個volatile寫操作的后面插入一個StoreLoad屏障。
  3. 在每個volatile讀操作的后面插入一個LoadLoad屏障。
  4. 在每個volatile讀操作的后面插入一個LoadStore屏障

總結(jié)來說:

  1. volatile寫操作之前的操作不會被編譯器重排序到寫操作之后。
  2. volatile讀之后的操作不會被編譯器重排序到volatile讀操作之前。
  3. 第一個是volatile讀操作,第二個是volatile寫操作,不能重排序

2.3 volatile的使用場景

1.狀態(tài)標(biāo)志

用volatile修飾的boolean 變量來作為while循環(huán)的的判斷條件:當(dāng)這個變量被其他線程修改的時候能保證while循環(huán)能立即讀到。

2.一次性安全發(fā)布

初始化對象的正確步驟為:

  1. 1、分配對象的內(nèi)存空間
  2. 2、初始化對象
  3. 3、設(shè)置引用指向剛分配的內(nèi)存地址

然而由于重排序機(jī)制,可能導(dǎo)致2、3步驟重排序,導(dǎo)致初始化對象的步驟變?yōu)?1-3-2。
著名的雙重檢查鎖定存在的問題就是因?yàn)槌跏蓟瘜ο蟮闹嘏判?,引用所指向的對象可能還沒有完成初始化,而僅僅是指向了一個空的內(nèi)存地址。

3.獨(dú)立觀察

這是第一種使用場景的引用。例如一種環(huán)境傳感器能夠感覺環(huán)境溫度。一個后臺線程可能會每隔幾秒讀取一次該傳感器,并更新包含當(dāng)前文檔的 volatile 變量。然后,其他線程可以讀取這個變量,從而隨時能夠看到最新的溫度值。

4.開銷較低的讀-寫鎖策略

前面我們介紹過,因?yàn)?++x 實(shí)際上是三種操作(讀、添加、存儲)的簡單組合,如果多個線程湊巧試圖同時對 volatile 計數(shù)器執(zhí)行增量操作,那么它的更新值有可能會丟失。但是被volatile修飾變量的讀 / 寫卻是原子操作。所以當(dāng)共享變量被volatile修飾之后,我們只需要在復(fù)合操作的方法上加上synchronized比直接用synchronized修飾該變量效率高的多。

2.4 volatile總結(jié)

相對于synchronized塊的代碼鎖,volatile應(yīng)該是提供了一個輕量級的針對共享變量的鎖,當(dāng)我們在多個線程間使用共享變量進(jìn)行通信的時候需要考慮將共享變量用volatile來修飾。

volatile是一種稍弱的同步機(jī)制,在訪問volatile變量時不會執(zhí)行加鎖操作,也就不會執(zhí)行線程阻塞,因此volatilei變量是一種比synchronized關(guān)鍵字更輕量級的同步機(jī)制。

3 synchronized和volatile的區(qū)別

1、 volatile不會進(jìn)行加鎖操作:

volatile變量是一種稍弱的同步機(jī)制在訪問volatile變量時不會執(zhí)行加鎖操作,因此也就不會使執(zhí)行線程阻塞,因此volatile變量是一種比synchronized關(guān)鍵字更輕量級的同步機(jī)制。

2、volatile變量作用類似于同步變量讀寫操作:

從內(nèi)存可見性的角度看,寫入volatile變量相當(dāng)于退出同步代碼塊,而讀取volatile變量相當(dāng)于進(jìn)入同步代碼塊。

3、volatile不如synchronized安全:

在代碼中如果過度依賴volatile變量來控制狀態(tài)的可見性,通常會比使用鎖的代碼更脆弱,也更難以理解。僅當(dāng)volatile變量能簡化代碼的實(shí)現(xiàn)以及對同步策略的驗(yàn)證時,才應(yīng)該使用它。一般來說,用同步機(jī)制會更安全些。

4、volatile無法同時保證內(nèi)存可見性和原則性:

加鎖機(jī)制(即同步機(jī)制)既可以確??梢娦杂挚梢源_保原子性,而volatile變量只能確保可見性,原因是聲明為volatile的簡單變量如果當(dāng)前值與該變量以前的值相關(guān),那么volatile關(guān)鍵字不起作用,也就是說如下的表達(dá)式都不是原子操作:“count++”、“count = count+1”。

以上所述是小編給大家介紹的Synchronized與Volatile區(qū)別詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Java 8 中 Map 騷操作之 merge() 的使用方法

    Java 8 中 Map 騷操作之 merge() 的使用方法

    本文簡單介紹了一下Map.merge()的方法,除此之外,Java 8 中的HashMap實(shí)現(xiàn)方法使用了TreeNode和 紅黑樹,原理很相似,今天通過本文給大家介紹Java 8 中 Map 騷操作之 merge() 的用法 ,需要的朋友參考下吧
    2021-07-07
  • Java CRM系統(tǒng)用戶登錄功能實(shí)現(xiàn)代碼實(shí)例

    Java CRM系統(tǒng)用戶登錄功能實(shí)現(xiàn)代碼實(shí)例

    這篇文章主要介紹了Java CRM系統(tǒng)用戶登錄功能實(shí)現(xiàn)代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • Mockito 結(jié)合 Springboot 進(jìn)行應(yīng)用測試的方法詳解

    Mockito 結(jié)合 Springboot 進(jìn)行應(yīng)用測試的方法詳解

    這篇文章主要介紹了Mockito 結(jié)合 Springboot 進(jìn)行應(yīng)用測試的方法詳解,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • Java詳細(xì)分析梳理垃圾回收機(jī)制

    Java詳細(xì)分析梳理垃圾回收機(jī)制

    垃圾回收,顧名思義,便是將已經(jīng)分配出去的,但卻不再使用的內(nèi)存回收回來,以便能夠再次分配。在?Java?虛擬機(jī)的語境下,垃圾指的是死亡的對象所占據(jù)的堆空間
    2022-04-04
  • Spring Boot ActiveMQ發(fā)布/訂閱消息模式原理解析

    Spring Boot ActiveMQ發(fā)布/訂閱消息模式原理解析

    這篇文章主要介紹了Spring Boot ActiveMQ發(fā)布/訂閱消息模式原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-07-07
  • 詳解java如何調(diào)用代理ip

    詳解java如何調(diào)用代理ip

    在進(jìn)行網(wǎng)絡(luò)爬蟲、數(shù)據(jù)采集或是訪問網(wǎng)站時,使用代理IP顯得尤為重要,本文將深入探討如何在Java中調(diào)用代理IP,感興趣的小伙伴可以了解一下
    2024-11-11
  • 淺入淺出的講解Spring循環(huán)依賴問題

    淺入淺出的講解Spring循環(huán)依賴問題

    循環(huán)依賴其實(shí)就是循環(huán)引用,也就是兩個或則兩個以上的bean互相持有對方,最終形成閉環(huán),下面這篇文章主要給大家介紹了關(guān)于Spring循環(huán)依賴問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-10-10
  • Springboot公共字段填充及ThreadLocal模塊改進(jìn)方案

    Springboot公共字段填充及ThreadLocal模塊改進(jìn)方案

    這篇文章主要為大家介紹了Springboot公共字段填充及ThreadLocal模塊改進(jìn)方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • 在webservice里調(diào)用耗時方法出錯的解決方案

    在webservice里調(diào)用耗時方法出錯的解決方案

    這篇文章主要介紹了在webservice里調(diào)用耗時方法出錯的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java使用阿里云接口進(jìn)行身份證實(shí)名認(rèn)證的示例實(shí)現(xiàn)

    Java使用阿里云接口進(jìn)行身份證實(shí)名認(rèn)證的示例實(shí)現(xiàn)

    這篇文章主要介紹了使用阿里云接口進(jìn)行身份證實(shí)名認(rèn)證的示例實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07

最新評論

龙岩市| 桂林市| 杭州市| 交口县| 连山| 阿尔山市| 绵阳市| 定边县| 巴楚县| 交城县| 赤城县| 安溪县| 奉新县| 海门市| 贵港市| 炎陵县| 唐河县| 三台县| 鄢陵县| 寿宁县| 晋州市| 米脂县| 大化| 甘肃省| 繁峙县| 安多县| 桂东县| 抚顺市| 长阳| 会理县| 清远市| 黄浦区| 姚安县| 泰安市| 桐乡市| 长宁县| 梨树县| 揭阳市| 湘西| 常州市| 东方市|