亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

java多線程volatile內(nèi)存語義解析

 更新時(shí)間:2020年01月08日 09:12:45   作者:sowhat1943  
這篇文章主要介紹了java多線程volatile內(nèi)存語義解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

這篇文章主要介紹了java多線程volatile內(nèi)存語義解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

  volatile關(guān)鍵字是java虛擬機(jī)提供的最輕量級(jí)額的同步機(jī)制。由于volatile關(guān)鍵字與java內(nèi)存模型相關(guān),因此,我們?cè)诮榻Bvolatile關(guān)鍵字之前,對(duì)java內(nèi)存模型進(jìn)行更多的補(bǔ)充(之前的博文也曾介紹過)。

  1. java內(nèi)存模型(JMM)

  JMM是一種規(guī)范,主要用于定義共享變量的訪問規(guī)則,目的是解決多個(gè)線程本地內(nèi)存與共享內(nèi)存的數(shù)據(jù)不一致、編譯器處理器的指令重排序造成的各種線程安全問題,以保障多線程編程的原子性、可見性和有序性。

  JMM規(guī)定了所有的變量都存儲(chǔ)在主內(nèi)存中,每條線程還有自己的工作內(nèi)存,線程中的工作內(nèi)存中存儲(chǔ)了該線程用到的變量的主內(nèi)存的拷貝,各線程對(duì)變量的所有操作都必須在工作內(nèi)存中進(jìn)行,
線程之間的變量值的傳遞都必須通過主內(nèi)存來進(jìn)行。

  JMM定義了8中操作實(shí)現(xiàn)主內(nèi)存與工作內(nèi)存的交互協(xié)議:

  •     1)lock:作用于主內(nèi)存,它把一個(gè)變量標(biāo)識(shí)為一條線程的獨(dú)占狀態(tài)。
  •     2)unlock:作用于主內(nèi)存,它把一個(gè)處于鎖定狀態(tài)的變量的釋放出來。
  •     3)read:作用于主內(nèi)存,它把一個(gè)變量的值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存中。
  •     4)load:作用于工作內(nèi)存,它把從主內(nèi)存中read到的值放入工作內(nèi)存的變量副本中。
  •     5)use:作用于工作內(nèi)存,它把一個(gè)變量的值從主內(nèi)存?zhèn)鬟f給執(zhí)行引擎
  •     6)assign:作用與工作內(nèi)存,它把一個(gè)從執(zhí)行引擎接收到的值賦值給工作內(nèi)存的變量。
  •     7)store:作用于工作內(nèi)存,把工作內(nèi)存中一個(gè)變量的值傳送到主內(nèi)存。
  •     8)write:作用于主內(nèi)存,它把store操作從工作內(nèi)存中得到的值放入主內(nèi)存中的變量中。

  這8中操作以及對(duì)著8中操作的規(guī)則的限制就能確定哪些內(nèi)存訪問在并發(fā)條件下是線程安全的,這種方式比較繁瑣,jdk1.5之后提出了提出了happens-before規(guī)則來判斷線程是否安全。

  可以這么理解,happens-before規(guī)則是JMM的核心.Happens-before就是用來確定兩個(gè)操作的執(zhí)行順序。這兩個(gè)操作可在同一線程中,也可以在兩個(gè)線程中。

happens-before規(guī)定:如果一個(gè)操作happens-before另個(gè)一操作,那么第一個(gè)操作的結(jié)果對(duì)第二個(gè)操作可見(但這并不意味著處理器必須按照happens-before順序執(zhí)行,只要不改變執(zhí)行結(jié)果,可任意優(yōu)化)。happens-before規(guī)則已在前邊博文中介紹,這里不再重復(fù)(http://www.cnblogs.com/gdy1993/p/9117331.html

  JMM內(nèi)存規(guī)則僅僅是一種規(guī)則,規(guī)則的最終落實(shí)是通過java虛擬機(jī)、編譯器以及處理器一同協(xié)作來落實(shí)的,而內(nèi)存屏障是java虛擬機(jī)、編譯器、處理器之間溝通的紐帶。

而java原因封裝了這些底層的具體實(shí)現(xiàn)與控制,提供了synchronized、lock和volatile等關(guān)鍵字的來保障多線程安全問題。

  2. volatile關(guān)鍵字

  (1)volatile對(duì)可見性的保證

  在介紹volatile關(guān)鍵字之前,先來看這樣一段代碼:

//線程1
    boolean stop = false;
    while(!stop) {
      doSomething();
    }
    //線程2
    stop = true;

  有兩個(gè)線程:線程1和線程2,線程1在stop==false時(shí),不停的執(zhí)行doSomething()方法;線程2在執(zhí)行到一定情況時(shí),將stop設(shè)置為true,將線程1中斷,很多人采用這種方式中斷線程,但這并不是安全的。因?yàn)閟top作為一個(gè)普通變量,線程2對(duì)其的修改,并不能立刻被線程1所感知,即線程1對(duì)stop的修改僅僅在自己的工作內(nèi)存中,還沒來的急寫入主內(nèi)存,線程2工作內(nèi)存中的stop并未修改,可能導(dǎo)致線程無法中斷,雖然這種可能性很小,但一旦發(fā)生,后果嚴(yán)重。

  而使用volatile變量修飾就能避免這個(gè)問題,這也是volatile第一個(gè)重要含義:

    volatile修飾的變量,能夠保證不同線程對(duì)這個(gè)變量操作的可見性,即一個(gè)線程修改了這個(gè)變量的值,這個(gè)新值對(duì)于其他線程是立即可見的。

    volatile的對(duì)可見性保證的原理:

  對(duì)于volatile修飾的變量,當(dāng)某個(gè)線程對(duì)其進(jìn)行修改時(shí),會(huì)強(qiáng)制將該值刷新到主內(nèi)存,這就使得其他線程對(duì)該變量在各自工作內(nèi)存中的緩存無效,因而在其他線程對(duì)該變量進(jìn)行操作時(shí),必須從主內(nèi)存中重新加載

  ?。?)volatile對(duì)原子性的保障?

  首先來看這樣一段代碼(深入理解java虛擬機(jī)):

public class VolatileTest {
  public static volatile int race = 0;
  
  public static void increase() {
    race++;
  }
  
  public static final int THREAD_COUNT = 20;
  
  public static void main(String[] args) {
    Thread[] threads = new Thread[THREAD_COUNT];
    for (Thread t : threads) {
      t = new Thread(new Runnable() {
        
        @Override
        public void run() {
          for(int i = 0; i < 10000; i++) {
            increase();
          }
        }
      });
      t.start();
    }
    
    while(Thread.activeCount() > 1) {
      Thread.yield();
    }
    
    System.out.println(race);//race < 200000
    
  }
}

  race是volatile修飾的共享變量,創(chuàng)建20個(gè)線程對(duì)這個(gè)共享變量進(jìn)行自增操作,每個(gè)線程自增的次數(shù)為10000次,如果volatile能夠保證原子性的話,最終race的結(jié)果肯定是200000。但結(jié)果不然,每次程序運(yùn)行race'的值總是小于200000,這也側(cè)面證明了volatile并不能保證共享變量操作的原子性。原理如下:

  線程1讀取了race的值,然后cp分配的時(shí)間片結(jié)束,線程2此時(shí)讀取了共享變量的值,并對(duì)race進(jìn)行自增操作,并將操作后的值刷新到主內(nèi)存,此時(shí)線程1已經(jīng)讀取了race的值,因此保留的依然是原來的值,此時(shí)這個(gè)值已是舊值,對(duì)race進(jìn)行自增操作后刷新到主內(nèi)存,因此主內(nèi)存中的值也是舊值。這也是volatile僅僅能保障讀到的是相對(duì)新值的原因。

 ?。?)volatile對(duì)有序性的保障

  首先來看這樣一段代碼:

//線程1
    boolean initialized = false;
    context = loadContext();
    initialized = true;
    //線程2
    while(!initialized) {
      sleep();
    }
    doSomething(context);

  線程2在initialized變量為true時(shí),使用context變量完成一些操作;線程1負(fù)責(zé)加載context,并在加載完成后將initialized變量設(shè)為true。但是,由于initialized只是一個(gè)普通變量,普通變量?jī)H僅能夠保證在該方法的執(zhí)行過程中,所有依賴賦值結(jié)果的地方都能獲得正確的值,而不能保證變量的賦值順序與程序代碼的執(zhí)行順序一致。因此就可能出現(xiàn)這樣一種情況,當(dāng)線程1將initialized變量設(shè)為true時(shí),context依然沒有加載完成,但線程2由于讀到initialized為true,就可能執(zhí)行了doSomething()方法,可能會(huì)產(chǎn)生非常奇怪的效果。

  而volatile的第二個(gè)語義就是禁止重排序: 

    寫volatile變量的操作與該操作之前的任何讀寫操作都不會(huì)被重排序;

    讀volatile變量操作與該操作之后的任何讀寫操作都不會(huì)重排序。

 ?。?) volatile的底層實(shí)現(xiàn)原理

  java語言底層是通過內(nèi)存屏障來實(shí)現(xiàn)volatile語義的。

  對(duì)于volatile變量的寫操作:

  ①java虛擬機(jī)會(huì)在該操作之前插入一個(gè)釋放屏障(loadstore+storestore),釋放屏障禁止了volatile變量的寫操作與該操作之前的任何讀寫操作的重排序。

 ?、趈ava虛擬機(jī)會(huì)在該操作之后插入一個(gè)存儲(chǔ)屏障(storeload),存儲(chǔ)屏障使得對(duì)volatile變量的寫操作能夠同步到主內(nèi)存。

  對(duì)于volatile變量的讀操作:

  ③java虛擬機(jī)會(huì)在該操作之前插入一個(gè)loadload,使得每次對(duì)volatile變量的讀取都從主內(nèi)存中重新加載(刷新處理器緩存)

  ④java虛擬機(jī)會(huì)在該操作之后插入一個(gè)獲得屏障(loadstore+loadload),使得volatile后的任何讀寫操作與該操作進(jìn)行重排序。

 ?、佗郾U峡梢娦裕冖鼙U嫌行蛐?。

  (5)volatile關(guān)鍵字與happens-before的關(guān)系

  Happens-before規(guī)則中的volatile規(guī)則為:對(duì)于一個(gè)volatile域的寫happens-before后續(xù)每一個(gè)針對(duì)該變量的讀操作。

  寫線程執(zhí)行write(),然后讀線程執(zhí)行read()方法,圖中每個(gè)箭頭都代表一個(gè)happens-before關(guān)系,黑色箭頭是根據(jù)程序順序規(guī)則,藍(lán)色箭頭根據(jù)volatile規(guī)則,紅色箭頭是根據(jù)傳遞性推出的,即操作2happens-before操作3,即對(duì)volatile共享變量的更新操作排在后續(xù)讀取操作之前,對(duì)volatile變量的修改對(duì)后續(xù)volatile變量的讀取可見。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java實(shí)現(xiàn)List去重的幾種方法總結(jié)

    Java實(shí)現(xiàn)List去重的幾種方法總結(jié)

    這篇文章主要為大家詳細(xì)介紹了Java中List去重的幾種常用方法總結(jié),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)和參考價(jià)值,需要的小伙伴可以了解一下
    2023-09-09
  • SpringBoot?整合Security權(quán)限控制的初步配置

    SpringBoot?整合Security權(quán)限控制的初步配置

    這篇文章主要為大家介紹了SpringBoot?整合Security權(quán)限控制的初步配置實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • BeanDefinitionRegistryPostProcessor如何動(dòng)態(tài)注冊(cè)Bean到Spring

    BeanDefinitionRegistryPostProcessor如何動(dòng)態(tài)注冊(cè)Bean到Spring

    這篇文章主要介紹了BeanDefinitionRegistryPostProcessor如何動(dòng)態(tài)注冊(cè)Bean到Spring,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • request.getParameter()方法的簡(jiǎn)單理解與運(yùn)用方式

    request.getParameter()方法的簡(jiǎn)單理解與運(yùn)用方式

    在JavaWeb開發(fā)中,request對(duì)象扮演著至關(guān)重要的角色,它是HTTP請(qǐng)求的封裝,request.getParameter()用于獲取客戶端通過GET或POST方式發(fā)送的參數(shù),與之相對(duì),request.setAttribute()用于在服務(wù)器端設(shè)置屬性,這些屬性只在一次請(qǐng)求中有效
    2024-10-10
  • spring-Kafka中的@KafkaListener深入源碼解讀

    spring-Kafka中的@KafkaListener深入源碼解讀

    本文主要通過深入了解源碼,梳理從spring啟動(dòng)到真正監(jiān)聽kafka消息的這套流程,從spring啟動(dòng)開始處理@KafkaListener,本文結(jié)合實(shí)例流程圖給大家講解的非常詳細(xì),需要的朋友參考下
    2023-02-02
  • SpringMvc響應(yīng)數(shù)據(jù)及結(jié)果視圖實(shí)現(xiàn)代碼

    SpringMvc響應(yīng)數(shù)據(jù)及結(jié)果視圖實(shí)現(xiàn)代碼

    這篇文章主要介紹了SpringMvc響應(yīng)數(shù)據(jù)及結(jié)果視圖實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • 比較Java數(shù)組和各種List的性能小結(jié)

    比較Java數(shù)組和各種List的性能小結(jié)

    這篇文章主要是分別對(duì)Java數(shù)組、ArrayList、LinkedList和Vector進(jìn)行隨機(jī)訪問和迭代等操作,并比較這種集合的性能。有需要的可以參考借鑒。
    2016-08-08
  • 詳解解密Java中的類型轉(zhuǎn)換問題

    詳解解密Java中的類型轉(zhuǎn)換問題

    這篇文章主要介紹了Java中的類型轉(zhuǎn)換問題,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • springboot單獨(dú)使用feign簡(jiǎn)化接口調(diào)用方式

    springboot單獨(dú)使用feign簡(jiǎn)化接口調(diào)用方式

    這篇文章主要介紹了springboot單獨(dú)使用feign簡(jiǎn)化接口調(diào)用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • SpringCloud客戶端的負(fù)載均衡Ribbon的實(shí)現(xiàn)

    SpringCloud客戶端的負(fù)載均衡Ribbon的實(shí)現(xiàn)

    微服務(wù)架構(gòu),不可避免的存在單個(gè)微服務(wù)有多個(gè)實(shí)例,這篇文章主要介紹了SpringCloud客戶端的負(fù)載均衡Ribbon的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-06-06

最新評(píng)論