国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - JAVA教程 - 詳解Java面試官最愛問的volatile關鍵字

詳解Java面試官最愛問的volatile關鍵字

2021-03-25 10:09mengwei JAVA教程

這篇文章主要介紹了詳解Java面試官最愛問的volatile關鍵字,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下

本文向大家分享的主要內容是Java面試中一個常見的知識點:volatile關鍵字。本文詳細介紹了volatile關鍵字的方方面面,希望大家在閱讀過本文之后,能完美解決volatile關鍵字的相關問題。

 在Java相關的崗位面試中,很多面試官都喜歡考察面試者對Java并發的了解程度,而以volatile關鍵字作為一個小的切入點,往往可以一問到底,把Java內存模型(JMM),Java并發編程的一些特性都牽扯出來,深入地話還可以考察JVM底層實現以及操作系統的相關知識。 下面我們以一次假想的面試過程,來深入了解下volitile關鍵字吧!

 

面試官: Java并發這塊了解的怎么樣?說說你對volatile關鍵字的理解

就我理解的而言,被volatile修飾的共享變量,就具有了以下兩點特性:

1.保證了不同線程對該變量操作的內存可見性;
2.禁止指令重排序

 

面試官: 能不能詳細說下什么是內存可見性,什么又是重排序呢?

這個聊起來可就多了,我還是從Java內存模型說起吧。 Java虛擬機規范試圖定義一種Java內存模型(JMM),來屏蔽掉各種硬件和操作系統的內存訪問差異,讓Java程序在各種平臺上都能達到一致的內存訪問效果。簡單來說,由于CPU執行指令的速度是很快的,但是內存訪問的速度就慢了很多,相差的不是一個數量級,所以搞處理器的那群大佬們又在CPU里加了好幾層高速緩存。 在Java內存模型里,對上述的優化又進行了一波抽象。JMM規定所有變量都是存在主存中的,類似于上面提到的普通內存,每個線程又包含自己的工作內存,方便理解就可以看成CPU上的寄存器或者高速緩存。所以線程的操作都是以工作內存為主,它們只能訪問自己的工作內存,且工作前后都要把值在同步回主內存。 這么說得我自己都有些不清楚了,拿張紙畫一下:

詳解Java面試官最愛問的volatile關鍵字

在線程執行時,首先會從主存中read變量值,再load到工作內存中的副本中,然后再傳給處理器執行,執行完畢后再給工作內存中的副本賦值,隨后工作內存再把值傳回給主存,主存中的值才更新。 使用工作內存和主存,雖然加快的速度,但是也帶來了一些問題。比如看下面一個例子:

?
1
i = i + 1;

假設i初值為0,當只有一個線程執行它時,結果肯定得到1,當兩個線程執行時,會得到結果2嗎?這倒不一定了。可能存在這種情況:

?
1
2
3
4
5
6
線程1: load i from 主存  // i = 0
    i + 1 // i = 1
線程2: load i from主存 // 因為線程1還沒將i的值寫回主存,所以i還是0
    i + 1 //i = 1
線程1: save i to 主存
線程2: save i to 主存

如果兩個線程按照上面的執行流程,那么i最后的值居然是1了。如果最后的寫回生效的慢,你再讀取i的值,都可能是0,這就是緩存不一致問題。 下面就要提到你剛才問到的問題了,JMM主要就是圍繞著如何在并發過程中如何處理原子性、可見性和有序性這3個特征來建立的,通過解決這三個問題,可以解除緩存不一致的問題。而volatile跟可見性和有序性都有關。

 

面試官:那你具體說說這三個特性呢?

1.原子性(Atomicity): Java中,對基本數據類型的讀取和賦值操作是原子性操作,所謂原子性操作就是指這些操作是不可中斷的,要做一定做完,要么就沒有執行。 比如:

?
1
2
3
4
i = 2;
j = i;
i++;
i = i + 1;

上面4個操作中,i=2是讀取操作,必定是原子性操作,j=i你以為是原子性操作,其實吧,分為兩步,一是讀取i的值,然后再賦值給j,這就是2步操作了,稱不上原子操作,i++和i = i + 1其實是等效的,讀取i的值,加1,再寫回主存,那就是3步操作了。所以上面的舉例中,最后的值可能出現多種情況,就是因為滿足不了原子性。 這么說來,只有簡單的讀取,賦值是原子操作,還只能是用數字賦值,用變量的話還多了一步讀取變量值的操作。有個例外是,虛擬機規范中允許對64位數據類型(long和double),分為2次32為的操作來處理,但是最新JDK實現還是實現了原子操作的。 JMM只實現了基本的原子性,像上面i++那樣的操作,必須借助于synchronized和Lock來保證整塊代碼的原子性了。線程在釋放鎖之前,必然會把i的值刷回到主存的。 2. 可見性(Visibility): 說到可見性,Java就是利用volatile來提供可見性的。 當一個變量被volatile修飾時,那么對它的修改會立刻刷新到主存,當其它線程需要讀取該變量時,會去內存中讀取新值。而普通變量則不能保證這一點。 其實通過synchronized和Lock也能夠保證可見性,線程在釋放鎖之前,會把共享變量值都刷回主存,但是synchronized和Lock的開銷都更大。 3. 有序性(Ordering) JMM是允許編譯器和處理器對指令重排序的,但是規定了as-if-serial語義,即不管怎么重排序,程序的執行結果不能改變。比如下面的程序段:

?
1
2
3
double pi = 3.14//A
double r = 1;    //B
double s= pi * r * r;//C

上面的語句,可以按照A->B->C執行,結果為3.14,但是也可以按照B->A->C的順序執行,因為A、B是兩句獨立的語句,而C則依賴于A、B,所以A、B可以重排序,但是C卻不能排到A、B的前面。JMM保證了重排序不會影響到單線程的執行,但是在多線程中卻容易出問題。 比如這樣的代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
int a = 0;
bool flag = false;
 
public void write() {
  a = 2;       //1
  flag = true;    //2
}
 
public void multiply() {
  if (flag) {     //3
    int ret = a * a;//4
  }
}

假如有兩個線程執行上述代碼段,線程1先執行write,隨后線程2再執行multiply,最后ret的值一定是4嗎?結果不一定:

詳解Java面試官最愛問的volatile關鍵字

如圖所示,write方法里的1和2做了重排序,線程1先對flag賦值為true,隨后執行到線程2,ret直接計算出結果,再到線程1,這時候a才賦值為2,很明顯遲了一步。 這時候可以為flag加上volatile關鍵字,禁止重排序,可以確保程序的有序性,也可以上重量級的synchronized和Lock來保證有序性,它們能保證那一塊區域里的代碼都是一次性執行完畢的。 另外,JMM具備一些先天的有序性,即不需要通過任何手段就可以保證的有序性,通常稱為happens-before原則。<<JSR-133:Java Memory Model and Thread Specification>>定義了如下happens-before規則: 1.程序順序規則: 一個線程中的每個操作,happens-before于該線程中的任意后續操作 2.監視器鎖規則:對一個線程的解鎖,happens-before于隨后對這個線程的加鎖 3.volatile變量規則: 對一個volatile域的寫,happens-before于后續對這個volatile域的讀 4.傳遞性:如果A happens-before B ,且 B happens-before C, 那么 A happens-before C 5.start()規則: 如果線程A執行操作ThreadB_start()(啟動線程B) , 那么A線程的ThreadB_start()happens-before 于B中的任意操作 6.join()原則: 如果A執行ThreadB.join()并且成功返回,那么線程B中的任意操作happens-before于線程A從ThreadB.join()操作成功返回。 7.interrupt()原則: 對線程interrupt()方法的調用先行發生于被中斷線程代碼檢測到中斷事件的發生,可以通過Thread.interrupted()方法檢測是否有中斷發生 8.finalize()原則:一個對象的初始化完成先行發生于它的finalize()方法的開始 第1條規則程序順序規則是說在一個線程里,所有的操作都是按順序的,但是在JMM里其實只要執行結果一樣,是允許重排序的,這邊的happens-before強調的重點也是單線程執行結果的正確性,但是無法保證多線程也是如此。 第2條規則監視器規則其實也好理解,就是在加鎖之前,確定這個鎖之前已經被釋放了,才能繼續加鎖。 第3條規則,就適用到所討論的volatile,如果一個線程先去寫一個變量,另外一個線程再去讀,那么寫入操作一定在讀操作之前。 第4條規則,就是happens-before的傳遞性。 后面幾條就不再一一贅述了。

 

面試官:volatile關鍵字如何滿足并發編程的三大特性的?

那就要重提volatile變量規則: 對一個volatile域的寫,happens-before于后續對這個volatile域的讀。 這條再拎出來說,其實就是如果一個變量聲明成是volatile的,那么當我讀變量時,總是能讀到它的最新值,這里最新值是指不管其它哪個線程對該變量做了寫操作,都會立刻被更新到主存里,我也能從主存里讀到這個剛寫入的值。也就是說volatile關鍵字可以保證可見性以及有序性。 繼續拿上面的一段代碼舉例:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
int a = 0;
bool flag = false;
 
public void write() {
  a = 2;       //1
  flag = true;    //2
}
 
public void multiply() {
  if (flag) {     //3
    int ret = a * a;//4
  }
}

這段代碼不僅僅受到重排序的困擾,即使1、2沒有重排序。3也不會那么順利的執行的。假設還是線程1先執行write操作,線程2再執行multiply操作,由于線程1是在工作內存里把flag賦值為1,不一定立刻寫回主存,所以線程2執行時,multiply再從主存讀flag值,仍然可能為false,那么括號里的語句將不會執行。 如果改成下面這樣:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
int a = 0;
volatile bool flag = false;
 
public void write() {
  a = 2;       //1
  flag = true;    //2
}
 
public void multiply() {
  if (flag) {     //3
    int ret = a * a;//4
  }
}

那么線程1先執行write,線程2再執行multiply。根據happens-before原則,這個過程會滿足以下3類規則: 程序順序規則:1 happens-before 2; 3 happens-before 4; (volatile限制了指令重排序,所以1 在2 之前執行) volatile規則:2 happens-before 3 傳遞性規則:1 happens-before 4 當寫一個volatile變量時,JMM會把該線程對應的本地內存中的共享變量刷新到主內存 當讀一個volatile變量時,JMM會把該線程對應的本地內存置為無效,線程接下來將從主內存中讀取共享變量。

 

面試官:volatile的兩點內存語義能保證可見性和有序性,但是能保證原子性嗎?

首先我回答是不能保證原子性,要是說能保證,也只是對單個volatile變量的讀/寫具有原子性,但是對于類似volatile++這樣的復合操作就無能為力了,比如下面的例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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) //保證前面的線程都執行完
      Thread.yield();
    System.out.println(test.inc);
  }

按道理來說結果是10000,但是運行下很可能是個小于10000的值。有人可能會說volatile不是保證了可見性啊,一個線程對inc的修改,另外一個線程應該立刻看到啊!可是這里的操作inc++是個復合操作啊,包括讀取inc的值,對其自增,然后再寫回主存。 假設線程A,讀取了inc的值為10,這時候被阻塞了,因為沒有對變量進行修改,觸發不了volatile規則。 線程B此時也讀讀inc的值,主存里inc的值依舊為10,做自增,然后立刻就被寫回主存了,為11。 此時又輪到線程A執行,由于工作內存里保存的是10,所以繼續做自增,再寫回主存,11又被寫了一遍。所以雖然兩個線程執行了兩次increase(),結果卻只加了一次。 有人說,volatile不是會使緩存行無效的嗎?但是這里線程A讀取到線程B也進行操作之前,并沒有修改inc值,所以線程B讀取的時候,還是讀的10。 又有人說,線程B將11寫回主存,不會把線程A的緩存行設為無效嗎?但是線程A的讀取操作已經做過了啊,只有在做讀取操作時,發現自己緩存行無效,才會去讀主存的值,所以這里線程A只能繼續做自增了。 綜上所述,在這種復合操作的情景下,原子性的功能是維持不了了。但是volatile在上面那種設置flag值的例子里,由于對flag的讀/寫操作都是單步的,所以還是能保證原子性的。 要想保證原子性,只能借助于synchronized,Lock以及并發包下的atomic的原子操作類了,即對基本數據類型的 自增(加1操作),自減(減1操作)、以及加法操作(加一個數),減法操作(減一個數)進行了封裝,保證這些操作是原子性操作。

 

面試官:說的還可以,那你知道volatile底層的實現機制?

如果把加入volatile關鍵字的代碼和未加入volatile關鍵字的代碼都生成匯編代碼,會發現加入volatile關鍵字的代碼會多出一個lock前綴指令。 lock前綴指令實際相當于一個內存屏障,內存屏障提供了以下功能: 1.重排序時不能把后面的指令重排序到內存屏障之前的位置 2.使得本CPU的Cache寫入內存 ** **3.寫入動作也會引起別的CPU或者別的內核無效化其Cache,相當于讓新寫入的值對別的線程可見。

 

面試官: 你在哪里會使用到volatile,舉兩個例子呢?

1.狀態量標記,就如上面對flag的標記,我重新提一下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
int a = 0;
volatile bool flag = false;
 
public void write() {
  a = 2;       //1
  flag = true;    //2
}
 
public void multiply() {
  if (flag) {     //3
    int ret = a * a;//4
  }
}

這種對變量的讀寫操作,標記為volatile可以保證修改對線程立刻可見。比synchronized,Lock有一定的效率提升。 2.單例模式的實現,典型的雙重檢查鎖定(DCL)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Singleton{
  private volatile static Singleton instance = null;
 
  private Singleton() {
 
  }
 
  public static Singleton getInstance() {
    if(instance==null) {
      synchronized (Singleton.class) {
        if(instance==null)
          instance = new Singleton();
      }
    }
    return instance;
  }
}

這是一種懶漢的單例模式,使用時才創建對象,而且為了避免初始化操作的指令重排序,給instance加上了volatile。

 

總結

以上就是本文關于詳解Java面試官最愛問的volatile關鍵字的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續參閱本站其他相關專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!

原文鏈接:http://www.techug.com/post/java-volatile-keyword.html

延伸 · 閱讀

精彩推薦
  • JAVA教程通過實例解析Java class文件編譯加載過程

    通過實例解析Java class文件編譯加載過程

    這篇文章主要介紹了通過實例解析Java class文件編譯加載過程,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋...

    AmourLee4032020-07-26
  • JAVA教程Java使用正則表達式去除小數點后面多余的0功能示例

    Java使用正則表達式去除小數點后面多余的0功能示例

    這篇文章主要介紹了Java使用正則表達式去除小數點后面多余的0功能,結合具體實例形式分析了java字符串正則替換相關操作技巧,需要的朋友可以參考下...

    zhangliao6134422020-11-04
  • JAVA教程spring boot打包成可執行jar包

    spring boot打包成可執行jar包

    本篇文章主要介紹了spring boot打包成可執行jar包,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧...

    0day__11642021-01-19
  • JAVA教程基于maven實現私服搭建步驟圖解

    基于maven實現私服搭建步驟圖解

    這篇文章主要介紹了基于maven實現私服搭建步驟圖解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參...

    護花使者5122020-08-05
  • JAVA教程java的poi技術讀取和導入Excel實例

    java的poi技術讀取和導入Excel實例

    本篇文章主要介紹了java的poi技術讀取和導入Excel實例,報表輸出是Java應用開發中經常涉及的內容,有需要的可以了解一下。 ...

    Hongten5572020-07-04
  • JAVA教程Object類wait及notify方法原理實例解析

    Object類wait及notify方法原理實例解析

    這篇文章主要介紹了Object類wait及notify方法原理實例解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可...

    javase-->2652020-08-21
  • JAVA教程java中queue接口的使用詳解

    java中queue接口的使用詳解

    本篇文章主要介紹了java中queue接口的使用詳解,對學習Queue接口有一定的幫助,感興趣的小伙伴們可以參考一下。 ...

    風生水起4392020-07-06
  • JAVA教程Java俄羅斯方塊小游戲

    Java俄羅斯方塊小游戲

    這篇文章主要為大家詳細介紹了Java俄羅斯方塊小游戲,實現了俄羅斯的經典功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們...

    qq_265252153332020-06-06
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 日本成人一区 | 欧美激情精品久久久久久变态 | 国产精品久久久99 | 国产精品99久久免费观看 | 一卡二卡久久 | 午夜影院网站 | 久久这里有精品视频 | 久久综合一区二区三区 | 99国产精品99久久久久久 | 久久精品2019中文字幕 | 亚洲日韩中文字幕一区 | 久久国产一区二区 | 国产精品欧美日韩 | 国产精品成人一区二区三区 | 日韩和的一区二在线 | 91精品国产一区二区三区香蕉 | 欧美日韩国产一区二区在线观看 | 国产精品爱久久久久久久 | 亚洲久久久久久 | 亚洲综合激情网 | 国产精品一区二区三区免费 | 成人免费视频 | 午夜精品久久久久久久久久久久 | 日韩一区在线播放 | 极品美女销魂一区二区三区 | 国产日韩欧美三级 | 欧美视频一二 | 一区二区三区四区在线 | 久久er99热精品一区二区 | 久久亚洲一区二区三区四区 | 五月在线视频 | 中文久久| 欧美日韩在线一区 | 亚洲精品7777xxxx青睐 | 久久久精品国产一区 | 日韩欧美久久 | 国产专区在线看 | 亚洲国产成人精品女人久久 | 日韩一区二区中文 | 精品视频网 | 日本一区二区免费在线播放 |