Java面試精講-volatile

多線程開發相關問題,是Java面試中一個非常高頻的面試點。除了基礎的線程相關的問題,在處理線程併發,線程同步,線程鎖這方面的面試題也越來越多,比如volatile就是其中之一。

下面舉幾個常見的volatile面試題:

1,Java 中能創建 volatile 數組嗎?

2,volatile 能使得一個非原子操作變成原子操作嗎?

3,volatile 類型變量提供什麼保證?

4,volatile和synchronized有什麼區別?

要回答這些問題,我們得理解什麼是volatile。本文由叩丁狼教育王一飛老師提供。

volatile 是JDK中的一個關鍵字,用於修飾成員變量或靜態成員變量, 使用volatile關鍵字修飾的變量在併發環境下具備2層特殊含義:

1>線程可見性

2>防止指令重排序

要想深入理解volatile關鍵字, 得先了解可見性,有序性

本文先解釋volatile的可見性問題:

可見性指線程操作某個共享變量,其他線程可以察覺到該變量的改變。看下例子:

public class Resource {

private boolean flag=false;

public void doSomething() {

System.out.println("進來了....");

while(!flag){

//不要打印任何指令,println具有獲取/釋放鎖動作

//System.out.println();

}

System.out.println("出去了....");

}

public void stop(){

System.out.println("停止了.....");

this.flag = true;

}

}

public class App {

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

final Resource resource = new Resource();

Thread t1 = new Thread(new Runnable() {

public void run() {

//線程t1執行打印動作

resource.doSomething();

}

}, "t1");

Thread t2 = new Thread(new Runnable() {

public void run() {

//線程t2執行終止

resource.stop();

}

}, "t2");

t1.start();

Thread.sleep(1000);

t2.start();

}

}

結果:

Java面試精講-volatile

線程t1啟動時,執行Resource中的doSomething方法,打印”進來了…..”,然後執行空循環,1s之後, 線程t2啟動,執行Resource中的stop方法, 目標改變flag變量的值,停止doSomething方法中空循環.最後結果發現並沒有停止,”出去了….” 沒有打印出來。為何?

分析:

導致上述問題主要原因是無法保證不同線程對共享變量flag操作的可見性。而要解釋這個可見性,那就必須從java內存模式說起。JVM在處理指令時,為提供指令的效率,會適當給共享變量做緩存處理。 JVM將所有的共享變量存儲在一個主存空間中,每一個線程也設置一個叫工作內存空間。如下圖:

Java面試精講-volatile

運行時,JVM會將共享變量flag複製多份,一份存放置到線程t1的工作內存中, 另一份放置到線程t2的工作內存中。當線程t1,線程t2對flag變量進行操作(讀寫操作)結束後,會將操作結果刷新到主存空間。需要注意的是,線程t1,t2修改的flag變量是各自工作緩存中的flag變量,它們相互是不可見的,即線程t1無法感知到線程t2的修改, 線程t2也無法感知得到t1的修改。

上面案例之所以線程t2執行了stop方法,修改了flag = true 依然無法讓線程t1停止的原因就是這個: 線程t2僅僅改動的是它自己的工作內存中的flag的值,沒有影響到線程t1, 線程t1工作內存中的flag依然是最初的備份flag= false.

解決:

private volatile boolean flag=false;

使用volatile修飾flag 變量,讓線程對共享變量的操作具有可見性。 volatile 修飾變量時,強迫所有線程每次操作共享變量flag時,都從主內存中獲取,這樣就可以做到某個線程對共享變量修改,其他線程都可見。

當理解了volatile關鍵字的含義,大家可以嘗試回答上面的四個面試題了,我們選擇兩個問題來回答:

1,Java 中能創建 volatile 數組嗎?

答:當然能,Java 中可以創建 volatile 類型數組,但是,volatile關鍵字只能是一個指向數組的引用,而不是整個數組。我的意思是,如果改變引用指向的數組,將會受到 volatile 的保護,但是如果多個線程同時改變數組的元素,volatile 標示符就不能起到之前的保護作用了。

2,volatile和synchronized有什麼區別?

答:1>volatile 是比synchronized更為輕量級同步機制,作用與變量, synchronized作用域代碼塊

2>volatile 無法保證原子性, 而synchronized可以

volatile int count;

count ++

多線程情況下,count操作存在線程安全問題,可以使用synchronized限制。


分享到:


相關文章: