中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

深入理解volatile關鍵字

發布時間:2020-08-01 04:39:30 來源:網絡 閱讀:283 作者:ckllf 欄目:編程語言

  1.volatile與可見性

  都知道volatile可以保證可見性,那么到底是如何保證的呢?

  這便于Happen-before原則有關,該原則的第三條規定:對一個volatile修飾的變量,寫操作要早于對這個變量的讀操作。具體步驟如下:

  A線程將共享變量讀進工作內存中,同時B線程也將共享變量讀進工作內存中。

  在A線程對共享變量修改后,會立即刷新到主內存,此時B線程的工作內存中的共享變量就會被設置無效,需要從主內存中重新讀取新值。反映到硬件上就是CPU的Cache line 置為無效狀態。

  這樣便保證了可見性,簡單而言,就是線程在對volatile修飾的變量修改且刷新到主內存之后,會使得其它線程的工作內存中的共享變量無效,需要從主內存中再此讀取。

  2.volatile與有序性

  都知道volatile可以保證有序性,那么到底是如何保證的呢?

  volatile保證有序性,比較直接,禁止JVM和處理器對volatile關鍵字修飾的變量進行指令重排序,但對于該變量之前或者之后的可以任意排序,只要最終的結果與沒更改前的結果保持一致即可。

  底層原理

  被volatile修飾的變量在底層會加一個“lock:”的前綴,帶"lock"前綴的指令相當于一個內存屏障,這恰恰是保證可見性與有序性的關鍵,該屏障的作用主要有一下幾點:

  指令重排時,屏障前的代碼不能重排到屏障后,屏障后的也不能重排到屏障前。

  執行到內存屏障時,確保前面的代碼都已經執行完畢,且執行結果是對屏障后的代碼可見的。

  強制將工作內存中的變量刷新到主內存。

  其它線程的工作內存的變量會設置無效,需要重現從主內存中讀取。

  3.volatile與原子性

  都知道volatile不能保證原子性,那么為何不能保證原子性呢?

  代碼演示:

  package com.github.excellent01;

  import java.util.concurrent.CountDownLatch;

  /**

  * @auther plg

  * @date 2019/5/19 9:37

  */

  public class TestVolatile implements Runnable {

  private volatile Integer num = 0;

  private static CountDownLatch latch = new CountDownLatch(10);

  @Override

  public void run() {

  for(int i = 0; i < 1000; i++){

  num++;

  }

  latch.countDown();

  }

  public Integer getNum() {

  return num;

  }

  public static void main(String[] args) throws InterruptedException {

  TestVolatile test = new TestVolatile();

  for(int i = 0; i < 10; i++){

  new Thread(test).start();

  }

  latch.await();

  System.out.println(test.getNum());

  }

  }

  啟動10個線程,每個線程對共享變量num,加1000次,當所有的線程執行完畢之后,打印輸出num 的最終結果。

  

深入理解volatile關鍵字


  很少有10000的這便是因為volatile不能保證原子性造成的。

  原因分析:

  num++的操作由三步組成:

  從主內存將num讀進工作內存中

  在工作內存中進行加一

  加一完成后,寫回主內存。

  雖然這三步都是原子操作,但合起來不是原子操作,每一步執行的過程中都有可能被打斷。

  假設此時num的值為10,線程A將變量讀進自己的工作內存中,此時發生了CPU切換,B也將num讀進自己的工作內存,此時值也是10.B線程在自己的工作內存中對num的值進行修改,變成了11,但此時還沒有刷新到主內存,因此A線程還不知道num的值已經發生了改變,之前所說的,對volatile變量修改后,其它線程會立即得知,前提也是要先刷新到主內存中,這時,其它線程才會將自己工作中的共享變量的值設為無效。因為沒有刷新到主內存,因此A傻傻的不知道,在10的基礎上加一,因此最終雖然兩個線程都進行了加一操作,但最終的結果只加了一次。

  這便是為什么volatile不能保證原子性。

  volatile的使用場景

  根據volatile的特點,保證有序性,可見性,不能保證原子性,因此volatile可以用于那些不需要原子性,或者說原子性已經得到保障的場合:

  代碼演示

  volatile boolean shutdownRequested

  public void shutdown() {

  shutdownRequested = true;

  }

  public void work() {

  while(shutdownRequested) {

  //do stuff

  }

  }

  只要線程對shutdownRequested進行修改,執行work的線程會立即看到,因此會立即停止下來,如果不加volatile的話,它每次去工作內存中讀取數據一直是個true,一直執行,都不知道別人已經讓它停了。

  代碼演示:無錫看婦科哪里好 http://www.xasgfk.cn/

  package com.github.excellent;

  import java.util.concurrent.ThreadPoolExecutor;

  /**

  * 啟動線程會被阻塞,flag 從內存讀入,會存入寄存器中,下次直接從寄存器取值

  * 因此值一直是false

  * 即使別的線程已經將值更改了,它也不知道

  * 加volatile即可。也可以加鎖,只要保證內存可見性即可

  * @auther plg

  * @date 2019/5/2 22:40

  */

  public class Testvolatile {

  public static boolean flag = false;

  public static void main(String[] args) throws InterruptedException {

  Thread thread1 = new Thread(()->{

  for(;;) {

  System.out.println(flag);

  }

  });

  Thread thread2 = new Thread(()->{

  for(;;){

  flag = true;

  }

  });

  thread1.start();

  Thread.sleep(1000);

  thread2.start();

  }

  }

  執行結果:

  加一個volatile就ok了。


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

大足县| 汾阳市| 神木县| 澄迈县| 汽车| 宝鸡市| 东乌珠穆沁旗| 怀宁县| 本溪市| 巫溪县| 庄河市| 泗阳县| 巴里| 马边| 三明市| 房产| 漠河县| 彭阳县| 樟树市| 观塘区| 舒兰市| 枞阳县| 曲阜市| 祁阳县| 峨眉山市| 玛多县| 通江县| 山丹县| 商南县| 津南区| 庄河市| 治多县| 湘乡市| 将乐县| 临泽县| 巫山县| 武义县| 甘谷县| 东平县| 新竹市| 乌兰察布市|