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

深入理解Java中的volatile關(guān)鍵字(總結(jié)篇)

 更新時(shí)間:2018年10月25日 10:58:42   作者:Gboy  
volatile這個(gè)關(guān)鍵字,不僅僅在Java語言中有,在很多語言中都有的,而且其用法和語義也都是不盡相同的。這篇文章主要介紹了Java中的volatile關(guān)鍵字,需要的朋友可以參考下

基本概念

--------------------------------------------------------------------------------

先補(bǔ)充一下概念:Java 內(nèi)存模型中的可見性、原子性和有序性。

可見性:

  可見性是一種復(fù)雜的屬性,因?yàn)榭梢娦灾械腻e(cuò)誤總是會違背我們的直覺。通常,我們無法確保執(zhí)行讀操作的線程能適時(shí)地看到其他線程寫入的值,有時(shí)甚至是根本不可能的事情。為了確保多個(gè)線程之間對內(nèi)存寫入操作的可見性,必須使用同步機(jī)制。

  可見性,是指線程之間的可見性,一個(gè)線程修改的狀態(tài)對另一個(gè)線程是可見的。也就是一個(gè)線程修改的結(jié)果。另一個(gè)線程馬上就能看到。比如:用volatile修飾的變量,就會具有可見性。volatile修飾的變量不允許線程內(nèi)部緩存和重排序,即直接修改內(nèi)存。所以對其他線程是可見的。但是這里需要注意一個(gè)問題,volatile只能讓被他修飾內(nèi)容具有可見性,但不能保證它具有原子性。比如 volatile int a = 0;之后有一個(gè)操作 a++;這個(gè)變量a具有可見性,但是a++ 依然是一個(gè)非原子操作,也就是這個(gè)操作同樣存在線程安全問題。

  在 Java 中 volatile、synchronized 和 final 實(shí)現(xiàn)可見性。

原子性:

  原子是世界上的最小單位,具有不可分割性。比如 a=0;(a非long和double類型) 這個(gè)操作是不可分割的,那么我們說這個(gè)操作時(shí)原子操作。再比如:a++; 這個(gè)操作實(shí)際是a = a + 1;是可分割的,所以他不是一個(gè)原子操作。非原子操作都會存在線程安全問題,需要我們使用同步技術(shù)(sychronized)來讓它變成一個(gè)原子操作。一個(gè)操作是原子操作,那么我們稱它具有原子性。java的concurrent包下提供了一些原子類,我們可以通過閱讀API來了解這些原子類的用法。比如:AtomicInteger、AtomicLong、AtomicReference等。

  在 Java 中 synchronized 和在 lock、unlock 中操作保證原子性。

有序性:

  Java 語言提供了 volatile 和 synchronized 兩個(gè)關(guān)鍵字來保證線程之間操作的有序性,volatile 是因?yàn)槠浔旧戆敖怪噶钪嘏判颉钡恼Z義,synchronized 是由“一個(gè)變量在同一個(gè)時(shí)刻只允許一條線程對其進(jìn)行 lock 操作”這條規(guī)則獲得的,此規(guī)則決定了持有同一個(gè)對象鎖的兩個(gè)同步塊只能串行執(zhí)行。

正題

在再有人問你Java內(nèi)存模型是什么,就把這篇文章發(fā)給他中我們曾經(jīng)介紹過,Java語言為了解決并發(fā)編程中存在的原子性、可見性和有序性問題,提供了一系列和并發(fā)處理相關(guān)的關(guān)鍵字,比如synchronized、volatile、final、concurren包等。在前一篇文章中,我們也介紹了synchronized的用法及原理。本文,來分析一下另外一個(gè)關(guān)鍵字——volatile。

本文就圍繞volatile展開,主要介紹volatile的用法、volatile的原理,以及volatile是如何提供可見性和有序性保障的等。

volatile這個(gè)關(guān)鍵字,不僅僅在Java語言中有,在很多語言中都有的,而且其用法和語義也都是不盡相同的。尤其在C語言、C++以及Java中,都有volatile關(guān)鍵字。都可以用來聲明變量或者對象。下面簡單來介紹一下Java語言中的volatile關(guān)鍵字。

volatile的用法

volatile通常被比喻成"輕量級的synchronized",也是Java并發(fā)編程中比較重要的一個(gè)關(guān)鍵字。和synchronized不同,volatile是一個(gè)變量修飾符,只能用來修飾變量。無法修飾方法及代碼塊等。

volatile的用法比較簡單,只需要在聲明一個(gè)可能被多線程同時(shí)訪問的變量時(shí),使用volatile修飾就可以了。

public class Singleton { 
  private volatile static Singleton singleton; 
  private Singleton (){} 
  public static Singleton getSingleton() { 
  if (singleton == null) { 
    synchronized (Singleton.class) { 
    if (singleton == null) { 
      singleton = new Singleton(); 
    } 
    } 
  } 
  return singleton; 
  } 
} 

如以上代碼,是一個(gè)比較典型的使用雙重鎖校驗(yàn)的形式實(shí)現(xiàn)單例的,其中使用volatile關(guān)鍵字修飾可能被多個(gè)線程同時(shí)訪問到的singleton。

volatile的原理

在再有人問你Java內(nèi)存模型是什么,就把這篇文章發(fā)給他中我們曾經(jīng)介紹過,為了提高處理器的執(zhí)行速度,在處理器和內(nèi)存之間增加了多級緩存來提升。但是由于引入了多級緩存,就存在緩存數(shù)據(jù)不一致問題。

但是,對于volatile變量,當(dāng)對volatile變量進(jìn)行寫操作的時(shí)候,JVM會向處理器發(fā)送一條lock前綴的指令,將這個(gè)緩存中的變量回寫到系統(tǒng)主存中。

但是就算寫回到內(nèi)存,如果其他處理器緩存的值還是舊的,再執(zhí)行計(jì)算操作就會有問題,所以在多處理器下,為了保證各個(gè)處理器的緩存是一致的,就會實(shí)現(xiàn)緩存一致性協(xié)議

緩存一致性協(xié)議:每個(gè)處理器通過嗅探在總線上傳播的數(shù)據(jù)來檢查自己緩存的值是不是過期了,當(dāng)處理器發(fā)現(xiàn)自己緩存行對應(yīng)的內(nèi)存地址被修改,就會將當(dāng)前處理器的緩存行設(shè)置成無效狀態(tài),當(dāng)處理器要對這個(gè)數(shù)據(jù)進(jìn)行修改操作的時(shí)候,會強(qiáng)制重新從系統(tǒng)內(nèi)存里把數(shù)據(jù)讀到處理器緩存里。

所以,如果一個(gè)變量被volatile所修飾的話,在每次數(shù)據(jù)變化之后,其值都會被強(qiáng)制刷入主存。而其他處理器的緩存由于遵守了緩存一致性協(xié)議,也會把這個(gè)變量的值從主存加載到自己的緩存中。這就保證了一個(gè)volatile在并發(fā)編程中,其值在多個(gè)緩存中是可見的。

volatile與可見性

可見性是指當(dāng)多個(gè)線程訪問同一個(gè)變量時(shí),一個(gè)線程修改了這個(gè)變量的值,其他線程能夠立即看得到修改的值。

我們在再有人問你Java內(nèi)存模型是什么,就把這篇文章發(fā)給他中分析過:Java內(nèi)存模型規(guī)定了所有的變量都存儲在主內(nèi)存中,每條線程還有自己的工作內(nèi)存,線程的工作內(nèi)存中保存了該線程中是用到的變量的主內(nèi)存副本拷貝,線程對變量的所有操作都必須在工作內(nèi)存中進(jìn)行,而不能直接讀寫主內(nèi)存。不同的線程之間也無法直接訪問對方工作內(nèi)存中的變量,線程間變量的傳遞均需要自己的工作內(nèi)存和主存之間進(jìn)行數(shù)據(jù)同步進(jìn)行。所以,就可能出現(xiàn)線程1改了某個(gè)變量的值,但是線程2不可見的情況。

前面的關(guān)于volatile的原理中介紹過了,Java中的volatile關(guān)鍵字提供了一個(gè)功能,那就是被其修飾的變量在被修改后可以立即同步到主內(nèi)存,被其修飾的變量在每次是用之前都從主內(nèi)存刷新。因此,可以使用volatile來保證多線程操作時(shí)變量的可見性。

volatile與有序性

有序性即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。

我們在再有人問你Java內(nèi)存模型是什么,就把這篇文章發(fā)給他中分析過:除了引入了時(shí)間片以外,由于處理器優(yōu)化和指令重排等,CPU還可能對輸入代碼進(jìn)行亂序執(zhí)行,比如load->add->save 有可能被優(yōu)化成load->save->add 。這就是可能存在有序性問題。

而volatile除了可以保證數(shù)據(jù)的可見性之外,還有一個(gè)強(qiáng)大的功能,那就是他可以禁止指令重排優(yōu)化等。

普通的變量僅僅會保證在該方法的執(zhí)行過程中所依賴的賦值結(jié)果的地方都能獲得正確的結(jié)果,而不能保證變量的賦值操作的順序與程序代碼中的執(zhí)行順序一致。

volatile可以禁止指令重排,這就保證了代碼的程序會嚴(yán)格按照代碼的先后順序執(zhí)行。這就保證了有序性。被volatile修飾的變量的操作,會嚴(yán)格按照代碼順序執(zhí)行,load->add->save 的執(zhí)行順序就是:load、add、save。

volatile與原子性

原子性是指一個(gè)操作是不可中斷的,要全部執(zhí)行完成,要不就都不執(zhí)行。

我們在Java的并發(fā)編程中的多線程問題到底是怎么回事兒?中分析過:線程是CPU調(diào)度的基本單位。CPU有時(shí)間片的概念,會根據(jù)不同的調(diào)度算法進(jìn)行線程調(diào)度。當(dāng)一個(gè)線程獲得時(shí)間片之后開始執(zhí)行,在時(shí)間片耗盡之后,就會失去CPU使用權(quán)。所以在多線程場景下,由于時(shí)間片在線程間輪換,就會發(fā)生原子性問題。

在上一篇文章中,我們介紹synchronized的時(shí)候,提到過,為了保證原子性,需要通過字節(jié)碼指令monitorenter和monitorexit,但是volatile和這兩個(gè)指令之間是沒有任何關(guān)系的。

所以,volatile是不能保證原子性的。

在以下兩個(gè)場景中可以使用volatile來代替synchronized:

1、運(yùn)算結(jié)果并不依賴變量的當(dāng)前值,或者能夠確保只有單一的線程會修改變量的值。

2、變量不需要與其他狀態(tài)變量共同參與不變約束。

除以上場景外,都需要使用其他方式來保證原子性,如synchronized或者concurrent包。

我們來看一下volatile和原子性的例子:

public class Test {
  public volatile int inc = 0;
  public void increase() {
    inc++;
  }
  public static void main(String[] args) {
    final Test test = new Test();
    for(int i=0;i<10;i++){
      new Thread(){
        public void run() {
          for(int j=0;j<1000;j++)
            test.increase();
        };
      }.start();
    }
    while(Thread.activeCount()>1) //保證前面的線程都執(zhí)行完
      Thread.yield();
    System.out.println(test.inc);
  }
}

以上代碼比較簡單,就是創(chuàng)建10個(gè)線程,然后分別執(zhí)行1000次i++操作。正常情況下,程序的輸出結(jié)果應(yīng)該是10000,但是,多次執(zhí)行的結(jié)果都小于10000。這其實(shí)就是volatile無法滿足原子性的原因。

為什么會出現(xiàn)這種情況呢,那就是因?yàn)殡m然volatile可以保證inc在多個(gè)線程之間的可見性。但是無法inc++的原子性。

總結(jié)與思考

我們介紹過了volatile關(guān)鍵字和synchronized關(guān)鍵字?,F(xiàn)在我們知道,synchronized可以保證原子性、有序性和可見性。而volatile卻只能保證有序性和可見性。

那么,我們再來看一下雙重校驗(yàn)鎖實(shí)現(xiàn)的單例,已經(jīng)使用了synchronized,為什么還需要volatile?

public class Singleton { 
  private volatile static Singleton singleton; 
  private Singleton (){} 
  public static Singleton getSingleton() { 
  if (singleton == null) { 
    synchronized (Singleton.class) { 
    if (singleton == null) { 
      singleton = new Singleton(); 
    } 
    } 
  } 
  return singleton; 
  } 
}

總結(jié)

以上所述是小編給大家介紹的Java中的volatile關(guān)鍵字,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關(guān)文章

  • SpringBoot利用AOP實(shí)現(xiàn)一個(gè)日志管理詳解

    SpringBoot利用AOP實(shí)現(xiàn)一個(gè)日志管理詳解

    目前有這么個(gè)問題,有兩個(gè)系統(tǒng)CSP和OMS,這倆系統(tǒng)共用的是同一套日志操作:Log;目前想?yún)^(qū)分下這倆系統(tǒng)的日志操作,那沒辦法了,只能重寫一份Log的日志操作。本文就將利用AOP實(shí)現(xiàn)一個(gè)日志管理,需要的可以參考一下
    2022-09-09
  • java使用計(jì)算md5校驗(yàn)碼方式比較兩個(gè)文件是否相同

    java使用計(jì)算md5校驗(yàn)碼方式比較兩個(gè)文件是否相同

    MD5文件效驗(yàn)碼是一個(gè)判斷文件是否是相同文件的途徑,通過比較兩個(gè)文件的Md5效驗(yàn)碼是否相同來精確判斷兩個(gè)文件是否相同
    2014-04-04
  • 使用Log4j2代碼方式配置實(shí)現(xiàn)線程級動態(tài)控制

    使用Log4j2代碼方式配置實(shí)現(xiàn)線程級動態(tài)控制

    這篇文章主要介紹了使用Log4j2代碼方式配置實(shí)現(xiàn)線程級動態(tài)控制,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 教你怎么使用Java實(shí)現(xiàn)WebSocket

    教你怎么使用Java實(shí)現(xiàn)WebSocket

    這篇文章主要介紹了教你怎么使用Java WebSocket,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-05-05
  • Java GUI編程之貪吃蛇游戲簡單實(shí)現(xiàn)方法【附demo源碼下載】

    Java GUI編程之貪吃蛇游戲簡單實(shí)現(xiàn)方法【附demo源碼下載】

    這篇文章主要介紹了Java GUI編程之貪吃蛇游戲簡單實(shí)現(xiàn)方法,詳細(xì)分析了貪吃蛇游戲的具體實(shí)現(xiàn)步驟與相關(guān)注意事項(xiàng),并附帶demo源碼供讀者下載參考,需要的朋友可以參考下
    2017-09-09
  • SpringBoot統(tǒng)一返回JSON格式實(shí)現(xiàn)方法詳解

    SpringBoot統(tǒng)一返回JSON格式實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了SpringBoot統(tǒng)一返回JSON格式實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-02-02
  • java中類與對象的使用詳情

    java中類與對象的使用詳情

    這篇文章主要介紹了java中類與對象的使用詳情,面向?qū)ο笫峭ㄟ^類和對象去描述和代表萬千事物對象的,首先我們需要知道如何去定義一個(gè)類,下面文章我們詳細(xì)介紹兩者的使用方法,需要的小伙伴可以參考一下
    2022-05-05
  • java協(xié)變返回類型使用示例

    java協(xié)變返回類型使用示例

    在面向?qū)ο蟪绦蛟O(shè)計(jì)中,協(xié)變返回類型指的是子類中的成員函數(shù)的返回值類型不必嚴(yán)格等同于父類中被重寫的成員函數(shù)的返回值類型,而可以是更"狹窄"的類型
    2014-02-02
  • SpringBoot最常用的50個(gè)注解總結(jié)(全是干貨!)

    SpringBoot最常用的50個(gè)注解總結(jié)(全是干貨!)

    SpringBoot提供多種注解簡化配置與啟動流程,如@SpringBootAppication、@RestController、@RequestMapping等,這篇文章主要介紹了SpringBoot最常用的50個(gè)注解的相關(guān)資料,需要的朋友可以參考下
    2024-09-09
  • Java 分割字符串詳解及實(shí)例代碼

    Java 分割字符串詳解及實(shí)例代碼

    這篇文章主要介紹了 Java 分割字符串詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2016-12-12

最新評論

额尔古纳市| 彝良县| 开平市| 光山县| 双江| 巫山县| 清水县| 梨树县| 绥化市| 宁南县| 施秉县| 铜川市| 竹北市| 元谋县| 凤凰县| 华坪县| 龙里县| 江安县| 浙江省| 金山区| 大同县| 钟祥市| 盖州市| 德昌县| 宁都县| 肃宁县| 华容县| 临漳县| 许昌市| 麻栗坡县| 株洲县| 锡林郭勒盟| 太白县| 宜丰县| 西乡县| 尚志市| 韶关市| 内江市| 嫩江县| 竹溪县| 神池县|