乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      volatile關(guān)鍵字詳解

       頭號碼甲 2021-05-30

      volatile的三個(gè)特點(diǎn)

      1. 保證線程之間的可見性

      2. 禁止指令重排

      3. 不保證原子性

      可見性

      概念

      可見性是多線程場景中才討論的,它表示多線程環(huán)境中,當(dāng)一個(gè)線程修改了共享變量的值,其他線程能夠知道這個(gè)修改。

      為什么需要可見性

      緩存一致性問題:

      public class Test {
          public static void main(String[] args) {
              Mythread mythread = new Mythread();
      
              new Thread(() -> {
                  try {
                      //延時(shí)2s,確保進(jìn)入while循環(huán)
                      TimeUnit.SECONDS.sleep(2);
                      //num自增
                      mythread.increment();
                      System.out.println("Thread-" + Thread.currentThread().getName() +
                              " current num value:" + mythread.num);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }, "test").start();
      
              while(mythread.num == 0){ 
                  //dead
              }
      
              System.out.println("game over!!!");
          }
      }
      
      class Mythread{
          //不加volatile,主線程無法得知num的值發(fā)生了改變,從而陷入死循環(huán)
          volatile int num = 0;
      
          public void increment(){
              ++num;
          }
      }

      如上述代碼,如果不加volatile,程序運(yùn)行結(jié)果如下

      不加volatile

      加上volatile關(guān)鍵字后,程序運(yùn)行結(jié)果如下

      加上volatile

      解決方向:

      • 總線鎖:

        一次只有一個(gè)線程能通過總線進(jìn)行通信。(效率低,已棄用)

      • MESI緩存一致性協(xié)議,CPU總線嗅探機(jī)制(監(jiān)聽機(jī)制)

        有volatile修飾的共享變量在編譯器編譯后進(jìn)行讀寫操作時(shí),指令會多一個(gè)lock前綴,Lock前綴的指令在多核處理器下會引發(fā)兩件事情。


        (參考下面兩位大佬的博客)

        https://blog.csdn.net/jinjiniao1/article/details/100540277

        https://blog.csdn.net/qq_33522040/article/details/95319946

        • 每個(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í)候,會重新從系統(tǒng)內(nèi)存中吧數(shù)據(jù)讀到處理器緩存行里。

        • 處理器使用嗅探技術(shù)保證它的內(nèi)部緩存,系統(tǒng)內(nèi)存和其他處理器的緩存在總線上保持一致

        • 寫一個(gè)volatile變量時(shí),JMM(java共享內(nèi)存模型)會把該線程對應(yīng)的本地內(nèi)存中的共享變量值刷新到主內(nèi)存;

        • 當(dāng)讀一個(gè)volatile變量時(shí),JMM會把該線程對應(yīng)的本地內(nèi)存置為無效,線程接下來從主內(nèi)存中讀取共享變量。

      禁止指令重排

      指令重排概念

      編譯器和CPU在保證最終結(jié)果不變的情況下,對指令的執(zhí)行順序進(jìn)行重排序。

      指令重排的問題

      可以與雙重檢驗(yàn)實(shí)現(xiàn)單例模式聯(lián)系起來看:

      首先,一個(gè)對象的創(chuàng)建過程可大致分為以下三步:

      1. 分配內(nèi)存空間

      2. 執(zhí)行對象構(gòu)造方法,初始化對象

      3. 引用指向?qū)嵗龑ο笤诙阎械牡刂?/p>

      但是在實(shí)際執(zhí)行過程中,CPU可能會對上述步驟進(jìn)行優(yōu)化,進(jìn)行指令重排

      序1->3->2,從而導(dǎo)致引用指向了未初始化的對象,如果這個(gè)時(shí)候另外一個(gè)線

      程引用了該未初始化的對象(只執(zhí)行了1->3兩步),就會產(chǎn)生異常。

      不保證原子性

      為什么無法保證

      具體例子

      public class Test {
          public static void main(String[] args) {
              Mythread mythread = new Mythread();
              for(int i = 0; i < 6666; ++i){
                  new Thread(() -> {
                      try {
                          mythread.increment();
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }, "test").start();
              }
              System.out.println("Thread-" + Thread.currentThread().getName() +
                      " current num value:" + mythread.num);
          }
      }
      
      class Mythread{
          volatile int num = 0;
      
          public void increment(){
              ++num;
          }
      }

      上述代碼的運(yùn)行結(jié)果如下圖

      可以看到,循環(huán)執(zhí)行了6666次,但最后的結(jié)果為6663,說明在程序運(yùn)行過程中出

      現(xiàn)了重復(fù)的情況。

      解決方案

      1. 使用JUC中的Atomic類(之后會專門寫一篇學(xué)習(xí)筆記進(jìn)行闡述)

      2. 使用synchronized關(guān)鍵字修飾(不推薦)

      volatile保證可見性和解決指令重排的底層原理

      內(nèi)存屏障(內(nèi)存柵欄)

      組成

      內(nèi)存屏障分為兩種:Load Barrier 讀屏障Store Barrier 寫屏障

      4種類型屏障

      種類例子作用
      LoadLoad屏障Load1; LoadLoad; Load2保證Load1讀取操作讀取完畢后再去執(zhí)行Load2后續(xù)讀取操作
      LoadStore屏障Load1; LoadStore; Store2保證Load1讀取操作讀取完畢后再去執(zhí)行Load2后續(xù)寫入操作
      StoreStore屏障Store1; StoreStore; Store2保證Load1的寫入對所有處理器可見后再去執(zhí)行Load2后續(xù)寫入操作
      StoreLoad屏障Store1; StoreLoad; Load2保證Load1的寫入對所有處理器可見后再去執(zhí)行Load2后續(xù)讀取操作

      作用

      1. 保證特定操作的執(zhí)行順序 

        在每個(gè)volatile修飾的全局變量讀操作前插入LoadLoad屏障,在讀操作后插入LoadStore屏障

      2. 保證某些變量的內(nèi)存可見性

        在每個(gè)volatile修飾的全局變量寫操作前插入StoreStore屏障,在寫操作后插入StoreLoad屏障

        本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多