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

Java volatile關(guān)鍵字原理剖析與實(shí)例講解

 更新時間:2023年07月05日 11:06:53   作者:老鼠只愛大米  
volatile是Java提供的一種輕量級的同步機(jī)制,Java?語言包含兩種內(nèi)在的同步機(jī)制:同步塊(或方法)和?volatile?變量,本文將詳細(xì)為大家總結(jié)Java volatile關(guān)鍵字,通過詳細(xì)的代碼示例給大家介紹的非常詳細(xì),需要的朋友可以參考下

一、簡介

volatile是Java提供的一種輕量級的同步機(jī)制。Java 語言包含兩種內(nèi)在的同步機(jī)制:同步塊(或方法)和 volatile 變量,相比于synchronized(synchronized通常稱為重量級鎖),volatile更輕量級,因?yàn)?strong>它不會引起線程上下文的切換和調(diào)度。但是volatile 變量的同步性較差(有時它更簡單并且開銷更低),而且其使用也更容易出錯。

二、并發(fā)編程的3個基本概念

1.原子性

定義: 即一個操作或者多個操作要么全部執(zhí)行并且執(zhí)行的過程不會被任何因素打斷,要么就都不執(zhí)行。

原子性是拒絕多線程操作的,不論是多核還是單核,具有原子性的量,同一時刻只能有一個線程來對它進(jìn)行操作。簡而言之,在整個操作過程中不會被線程調(diào)度器中斷的操作,都可認(rèn)為是原子性。例如 a=1是原子性操作,但是a++和a +=1就不是原子性操作。Java中的原子性操作包括:

(1)基本類型的讀取和賦值操作,且賦值必須是值賦給變量,變量之間的相互賦值不是原子性操作。

(2)所有引用reference的賦值操作。

(3)java.concurrent.Atomic.* 包中所有類的一切操作。

2.可見性

定義:指當(dāng)多個線程訪問同一個變量時,一個線程修改了這個變量的值,其他線程能夠立即看得到修改的值。

在多線程環(huán)境下,一個線程對共享變量的操作對其他線程是不可見的。Java提供了volatile來保證可見性,當(dāng)一個變量被volatile修飾后,表示著線程本地內(nèi)存無效,當(dāng)一個線程修改共享變量后他會立即被更新到主內(nèi)存中,其他線程讀取共享變量時,會直接從主內(nèi)存中讀取。當(dāng)然,synchronize和Lock都可以保證可見性。synchronized和Lock能保證同一時刻只有一個線程獲取鎖然后執(zhí)行同步代碼,并且在釋放鎖之前會將對變量的修改刷新到主存當(dāng)中。因此可以保證可見性。

3.有序性

 定義:即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。

Java內(nèi)存模型中的有序性可以總結(jié)為:如果在本線程內(nèi)觀察,所有操作都是有序的;如果在一個線程中觀察另一個線程,所有操作都是無序的。前半句是指“線程內(nèi)表現(xiàn)為串行語義”,后半句是指“指令重排序”現(xiàn)象和“工作內(nèi)存主主內(nèi)存同步延遲”現(xiàn)象。

 在Java內(nèi)存模型中,為了效率是允許編譯器和處理器對指令進(jìn)行重排序,當(dāng)然重排序不會影響單線程的運(yùn)行結(jié)果,但是對多線程會有影響。Java提供volatile來保證一定的有序性。最著名的例子就是單例模式里面的DCL(雙重檢查鎖)。另外,可以通過synchronized和Lock來保證有序性,synchronized和Lock保證每個時刻是有一個線程執(zhí)行同步代碼,相當(dāng)于是讓線程順序執(zhí)行同步代碼,自然就保證了有序性。

三、鎖的互斥和可見性

鎖提供了兩種主要特性:互斥(mutual exclusion) 和可見性(visibility)。

(1)互斥即一次只允許一個線程持有某個特定的鎖,一次就只有一個線程能夠使用該共享數(shù)據(jù)。

(2)可見性要更加復(fù)雜一些,它必須確保釋放鎖之前對共享數(shù)據(jù)做出的更改對于隨后獲得該鎖的另一個線程是可見的。也即當(dāng)一條線程修改了共享變量的值,新值對于其他線程來說是可以立即得知的。如果沒有同步機(jī)制提供的這種可見性保證,線程看到的共享變  量可能是修改前的值或不一致的值,這將引發(fā)許多嚴(yán)重問題。要使 volatile 變量提供理想的線程安全,必須同時滿足下面兩個條件:

a.對變量的寫操作不依賴于當(dāng)前值。

b.該變量沒有包含在具有其他變量的不變式中。

實(shí)際上,這些條件表明,可以被寫入 volatile 變量的這些有效值獨(dú)立于任何程序的狀態(tài),包括變量的當(dāng)前狀態(tài)。事實(shí)上就是保證操作是原子性操作,才能保證使用volatile關(guān)鍵字的程序在并發(fā)時能夠正確執(zhí)行。

四、Java的內(nèi)存模型JMM以及共享變量的可見性

JMM決定一個線程對共享變量的寫入何時對另一個線程可見,JMM定義了線程和主內(nèi)存之間的抽象關(guān)系:共享變量存儲在主內(nèi)存(Main Memory)中,每個線程都有一個私有的本地內(nèi)存(Local Memory),本地內(nèi)存保存了被該線程使用到的主內(nèi)存的副本拷貝,線程對變量的所有操作都必須在工作內(nèi)存中進(jìn)行,而不能直接讀寫主內(nèi)存中的變量。

20180613171711160

對于普通的共享變量來講,線程A將其修改為某個值發(fā)生在線程A的本地內(nèi)存中,此時還未同步到主內(nèi)存中去;而線程B已經(jīng)緩存了該變量的舊值,所以就導(dǎo)致了共享變量值的不一致。解決這種共享變量在多線程模型中的不可見性問題,較粗暴的方式自然就是加鎖,但是此處使用synchronized或者Lock這些方式太重量級了,比較合理的方式其實(shí)就是volatile。

需要注意的是,JMM是個抽象的內(nèi)存模型,所以所謂的本地內(nèi)存,主內(nèi)存都是抽象概念,并不一定就真實(shí)的對應(yīng)cpu緩存和物理內(nèi)存

五、volatile變量的特性  

1.保證可見性,不保證原子性

(1)當(dāng)寫一個volatile變量時,JMM會把該線程本地內(nèi)存中的變量強(qiáng)制刷新到主內(nèi)存中去。

(2)這個寫操作會導(dǎo)致其他線程中的volatile變量緩存無效。

2.禁止指令重排

重排序是指編譯器和處理器為了優(yōu)化程序性能而對指令序列進(jìn)行排序的一種手段。重排序需要遵守一定規(guī)則:

(1)重排序操作不會對存在數(shù)據(jù)依賴關(guān)系的操作進(jìn)行重排序。

比如:a=1;b=a; 這個指令序列,由于第二個操作依賴于第一個操作,所以在編譯時和處理器運(yùn)行時這兩個操作不會被重排序。

(2)重排序是為了優(yōu)化性能,但是不管怎么重排序,單線程下程序的執(zhí)行結(jié)果不能被改變

比如:a=1;b=2;c=a+b這三個操作,第一步(a=1)和第二步(b=2)由于不存在數(shù)據(jù)依賴關(guān)系, 所以可能會發(fā)生重排序,但是c=a+b這個操作是不會被重排序的,因?yàn)樾枰WC最終的結(jié)果一定是c=a+b=3。

重排序在單線程下一定能保證結(jié)果的正確性,但是在多線程環(huán)境下,可能發(fā)生重排序,影響結(jié)果,下例中的1和2由于不存在數(shù)據(jù)依賴關(guān)系,則有可能會被重排序,先執(zhí)行status=true再執(zhí)行a=2。而此時線程B會順利到達(dá)4處,而線程A中a=2這個操作還未被執(zhí)行,所以b=a+1的結(jié)果也有可能依然等于2。

public class TestVolatile{
  int a = 1;
  boolean status = false;
  //狀態(tài)切換為true
  public void changeStatus{
    a = 2;   //1
    status = true;  //2
  }
  //若狀態(tài)為true,則為running
  public void run(){
    if(status){   //3
      int b = a + 1;  //4
      System.out.println(b);
    }
  }
}

使用volatile關(guān)鍵字修飾共享變量便可以禁止這種重排序。若用volatile修飾共享變量,在編譯時,會在指令序列中插入內(nèi)存屏障來禁止特定類型的處理器重排序,volatile禁止指令重排序也有一些規(guī)則:

a.當(dāng)程序執(zhí)行到volatile變量的讀操作或者寫操作時,在其前面的操作的更改肯定全部已經(jīng)進(jìn)行,且結(jié)果已經(jīng)對后面的操作可見;在其后面的操作肯定還沒有進(jìn)行;

b.在進(jìn)行指令優(yōu)化時,不能將對volatile變量訪問的語句放在其后面執(zhí)行,也不能把volatile變量后面的語句放到其前面執(zhí)行。

即執(zhí)行到volatile變量時,其前面的所有語句都執(zhí)行完,后面所有語句都未執(zhí)行。且前面語句的結(jié)果對volatile變量及其后面語句可見。

六、volatile不適用的場景

1.volatile不適合復(fù)合操作

例如,inc++不是一個原子性操作,可以由讀取、加、賦值3步組成,所以結(jié)果并不能達(dá)到30000。

20180613172347910

2.解決方法

(1)采用synchronized

20180613172407590

(2)采用Lock

20180613172430842

(3)采用java并發(fā)包中的原子操作類,原子操作類是通過CAS循環(huán)的方式來保證其原子性的

20180613172451222

七、volatile原理

volatile可以保證線程可見性且提供了一定的有序性,但是無法保證原子性。在JVM底層volatile是采用“內(nèi)存屏障”來實(shí)現(xiàn)的。觀察加入volatile關(guān)鍵字和沒有加入volatile關(guān)鍵字時所生成的匯編代碼發(fā)現(xiàn),加入volatile關(guān)鍵字時,會多出一個lock前綴指令,lock前綴指令實(shí)際上相當(dāng)于一個內(nèi)存屏障(也成內(nèi)存柵欄),內(nèi)存屏障會提供3個功能:

(1)它確保指令重排序時不會把其后面的指令排到內(nèi)存屏障之前的位置,也不會把前面的指令排到內(nèi)存屏障的后面;即在執(zhí)行到內(nèi)存屏障這句指令時,在它前面的操作已經(jīng)全部完成;

(2)它會強(qiáng)制將對緩存的修改操作立即寫入主存;

(3)如果是寫操作,它會導(dǎo)致其他CPU中對應(yīng)的緩存行無效。

八、單例模式的雙重鎖為什么要加volatile

public class TestInstance{
  private volatile static TestInstance instance;
  public static TestInstance getInstance(){   //1
    if(instance == null){   //2
      synchronized(TestInstance.class){   //3
        if(instance == null){   //4
          instance = new TestInstance(); //5
        }
      }
    }
    return instance;   //6
  }
}

需要volatile關(guān)鍵字的原因是,在并發(fā)情況下,如果沒有volatile關(guān)鍵字,在第5行會出現(xiàn)問題。instance = new TestInstance();可以分解為3行偽代碼

a. memory = allocate() //分配內(nèi)存
b. ctorInstanc(memory) //初始化對象
c. instance = memory   //設(shè)置instance指向剛分配的地址

上面的代碼在編譯運(yùn)行時,可能會出現(xiàn)重排序從a-b-c排序?yàn)閍-c-b。在多線程的情況下會出現(xiàn)以下問題。當(dāng)線程A在執(zhí)行第5行代碼時,B線程進(jìn)來執(zhí)行到第2行代碼。假設(shè)此時A執(zhí)行的過程中發(fā)生了指令重排序,即先執(zhí)行了a和c,沒有執(zhí)行b。那么由于A線程執(zhí)行了c導(dǎo)致instance指向了一段地址,所以B線程判斷instance不為null,會直接跳到第6行并返回一個未初始化的對象。

以上就是Java volatile關(guān)鍵字原理剖析與實(shí)例講解的詳細(xì)內(nèi)容,更多關(guān)于Java volatile關(guān)鍵字的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java中的構(gòu)造方法this、super的用法詳解

    Java中的構(gòu)造方法this、super的用法詳解

    這篇文章較詳細(xì)的給大家介紹了Java中的構(gòu)造方法this、super的用法,非常不錯,具有一定的參考借鑒價值,需要的朋友參考下吧
    2018-07-07
  • Mybatis-Plus的條件構(gòu)造器QueryWrapper & UpdateWrapper示例詳解

    Mybatis-Plus的條件構(gòu)造器QueryWrapper & UpdateWrapper示例詳解

    Mybatis-Plus的條件構(gòu)造器QueryWrapper和UpdateWrapper為開發(fā)者提供了強(qiáng)大、靈活的條件構(gòu)建工具,能夠大大簡化數(shù)據(jù)庫操作的代碼,通過本文的介紹,讀者可以更加深入地理解這兩個條件構(gòu)造器的使用方法,并在實(shí)際項(xiàng)目中靈活應(yīng)用,感興趣的朋友跟隨小編一起看看吧
    2024-01-01
  • Java中在時間戳計(jì)算的過程中遇到的數(shù)據(jù)溢出問題解決

    Java中在時間戳計(jì)算的過程中遇到的數(shù)據(jù)溢出問題解決

    這篇文章主要介紹了Java中在時間戳計(jì)算的過程中遇到的數(shù)據(jù)溢出問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • Spring Boot利用Thymeleaf發(fā)送Email的方法教程

    Spring Boot利用Thymeleaf發(fā)送Email的方法教程

    spring Boot默認(rèn)就是使用thymeleaf模板引擎的,下面這篇文章主要給大家介紹了關(guān)于在Spring Boot中利用Thymeleaf發(fā)送Email的方法教程,文中通過示例代碼介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。
    2017-08-08
  • Springboot遷移到Micronaut實(shí)現(xiàn)過程詳解

    Springboot遷移到Micronaut實(shí)現(xiàn)過程詳解

    這篇文章主要為大家?介紹了Springboot遷移到Micronaut實(shí)現(xiàn)過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • 原生java代碼實(shí)現(xiàn)碼云第三方驗(yàn)證登錄的示例代碼

    原生java代碼實(shí)現(xiàn)碼云第三方驗(yàn)證登錄的示例代碼

    這篇文章主要介紹了原生java代碼實(shí)現(xiàn)碼云第三方驗(yàn)證登錄的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • 一文讓你徹底學(xué)會Java左移、右移操作

    一文讓你徹底學(xué)會Java左移、右移操作

    這篇文章主要介紹了了Java中的位運(yùn)算符,包括左移(<<)、右移(>>)和無符號右移(>>>),左移將數(shù)字的二進(jìn)制位向左移動,相當(dāng)于乘以2的n次方;右移將數(shù)字的二進(jìn)制位向右移動,保留符號位,相當(dāng)于除以2的n次方,文中介紹的非常詳細(xì),需要的朋友可以參考下
    2025-02-02
  • Springboot中登錄后關(guān)于cookie和session攔截問題的案例分析

    Springboot中登錄后關(guān)于cookie和session攔截問題的案例分析

    這篇文章主要介紹了Springboot中登錄后關(guān)于cookie和session攔截案例,本文通過實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • Jenkins初級應(yīng)用之Invoke?Phing?targets插件配置

    Jenkins初級應(yīng)用之Invoke?Phing?targets插件配置

    這篇文章主要為大家介紹了Jenkins初級應(yīng)用之Invoke?Phing?targets的插件配置,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪<BR>
    2022-04-04
  • 詳解Java設(shè)計(jì)模式之外觀模式

    詳解Java設(shè)計(jì)模式之外觀模式

    在Java開發(fā)中,設(shè)計(jì)模式是一種十分常見的編程思想,它可以幫助我們解決很多實(shí)際開發(fā)中的問題,本篇文章將介紹一種常見的設(shè)計(jì)模式——外觀模式,并結(jié)合實(shí)際的開發(fā)場景進(jìn)行講解,需要的朋友可以參考下
    2023-06-06

最新評論