voliate關鍵字及其示例

voliate關鍵字

1 使變量在線程間可見

對于避免不可見性問題,Java還提供了一種弱形式的同步,即使用了volatile關鍵字。該關鍵字確保了對一個變量的更新對其他線程可見。當一個變量被聲明為volatile時候,線程寫入時候不會把值緩存在寄存器或者或者在其他地方,當線程讀取的時候會從主內存重新獲取最新值,而不是使用當前線程的拷貝內存變量值。volatile雖然提供了可見性保證,但是不能使用他來構建復合的原子性操作,也就是說當一個變量依賴其他變量或者更新變量值時候新值依賴當前老值時候不在適用。與synchronized相似之處在于如圖
如圖線程A修改了volatile變量b的值,然后線程B讀取了改變量值,那么所有A線程在寫入變量b值前可見的變量值,在B讀取volatile變量b后對線程B都是可見的,圖中線程B對A操作的變量a,b的值都可見的。volatile的內存語義和synchronized有類似之處,具體說是說當線程寫入了volatile變量值就等價于線程退出synchronized同步塊(會把寫入到本地內存的變量值同步到主內存),讀取volatile變量值就相當于進入同步塊(會先清空本地內存變量值,從主內存獲取最新值)。
轉自http://ifeve.com/%E9%AB%98%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%BF%85%E5%A4%87%E5%9F%BA%E7%A1%80/
/**
 * Created by lixiaodong on 2017/6/23.
 */
public class Test extends Thread{

     //voliate
    private String i ="sss";

    private void setI(String i){
        this.i=i;
    }
    @Override
    public void run() {
        System.out.println("進入方法"+i);
        while (i.equals("sss")){
//            System.out.println("方法執行");
//
//            try {
//                Thread.sleep(3000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
        }
        System.out.println("線程結束");
    }

    public static void main(String[] args ) throws InterruptedException{
        Test test=new Test();
        test.start();
        Thread.sleep(1000);
        System.out.println("線程設置了stop");
        test.setI("線程設置了stop");
    }
}

上面是一個簡單的示例。

  1. 首先運行代碼,可以看到,盡管將變量設置了stop,test線程并沒有如預期的停止.說明,test線程內的i的并沒有被修改,test只是在start時將i變量拷貝到了線程自有的一塊空間內,與主線內的i變量互不影響.
  2. 將voliate關鍵字放在變量i的聲明上,運行發現程序正常停止.可見變量i在任何一個線程內都是可見的,當變量i在主線程被修改時,子線程立即獲得了被更新的值.
  3. 最坑的地方來了,打開代碼中的while循環中的打印語句,將voliate關鍵字注釋掉,執行代碼.神奇的事情發生了,程序正常的停止了,WTF!(在我最開始研究voliate的時候,我一直有這句輸出語句,一直得不到正確結果)這是為啥呢?下面這段話基本說明了問題,同時你也可以將輸出語句注釋點,打開sleep的注釋,看看結果.
JVM會盡力保證內存的可見性,即便這個變量沒有加同步關鍵字。換句話說,只要CPU有時間,JVM會盡力去保證變量值的更新。這種與volatile關鍵字的不同在于,volatile關鍵字會強制的保證線程的可見性。而不加這個關鍵字,JVM也會盡力去保證可見性,但是如果CPU一直有其他的事情在處理,它也沒辦法。最開始的代碼,一直處于試了循環中,CPU處于一直被飽受占用的時候,這個時候CPU沒有時間,JVM也不能強制要求CPU分點時間去取最新的變量值。而加了System.out.println之后,由于內部代碼的同步關鍵字的存在,導致CPU的輸出其實是比較耗時的。這個時候CPU就有可能有時間去保證內存的可見性,于是while循環可以被終止。其實,也可以在while循環里面加上sleep,讓run方法放棄cpu,但是不放棄鎖,這個時候由于CPU有空閑的時候就去按照JVM的要求去保證內存的可見性。如下圖所示。 run方法里面休息了3秒,cpu有充足的空閑時間去取變量的最新值,所以循環執行一次就停止了。(轉自http://blog.csdn.net/weililansehudiefei/article/details/70904111)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容