程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 深刻解析Java中volatile症結字的感化

深刻解析Java中volatile症結字的感化

編輯:關於JAVA

深刻解析Java中volatile症結字的感化。本站提示廣大學習愛好者:(深刻解析Java中volatile症結字的感化)文章只能為提供參考,不一定能成為您想要的結果。以下是深刻解析Java中volatile症結字的感化正文


在java線程並發處置中,有一個症結字volatile的應用今朝存在很年夜的混雜,認為應用這個症結字,在停止多線程並發處置的時刻便可以萬事年夜吉。

Java說話是支撐多線程的,為懂得決線程並發的成績,在說話外部引入了 同步塊 和 volatile 症結字機制。

synchronized
同步塊年夜家都比擬熟習,經由過程 synchronized 症結字來完成,一切加上synchronized 和 塊語句,在多線程拜訪的時刻,統一時辰只能有一個線程可以或許用synchronized 潤飾的辦法 或許 代碼塊。

volatile
用volatile潤飾的變量,線程在每次應用變量的時刻,都邑讀取變量修正後的最的值。volatile很輕易被誤用,用來停止原子性操作。

上面看一個例子,我們完成一個計數器,每次線程啟動的時刻,會挪用計數器inc辦法,對計數器停止加一

履行情況——jdk版本:jdk1.6.0_31 ,內存 :3G   cpu:x86 2.4G

public class Counter {

    public static int count = 0;

    public static void inc() {

        //這裡延遲1毫秒,使得成果顯著
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
        }

        count++;
    }

    public static void main(String[] args) {

        //同時啟動1000個線程,去停止i++盤算,看看現實成果

        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Counter.inc();
                }
            }).start();
        }

        //這裡每次運轉的值都有能夠分歧,能夠為1000
        System.out.println("運轉成果:Counter.count=" + Counter.count);
    }
}

運轉成果:Counter.count=995
現實運算成果每次能夠都紛歧樣,本機的成果為:運轉成果:Counter.count=995,可以看出,在多線程的情況下,Counter.count並沒有希冀成果是1000

許多人認為,這個是多線程並提問題,只須要在變量count之前加上volatile便可以免這個成績,那我們在修正代碼看看,看看成果是否是相符我們的希冀

public class Counter {

    public volatile static int count = 0;

    public static void inc() {

        //這裡延遲1毫秒,使得成果顯著
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
        }

        count++;
    }

    public static void main(String[] args) {

        //同時啟動1000個線程,去停止i++盤算,看看現實成果

        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Counter.inc();
                }
            }).start();
        }

        //這裡每次運轉的值都有能夠分歧,能夠為1000
        System.out.println("運轉成果:Counter.count=" + Counter.count);
    }
}

運轉成果:Counter.count=992

運轉成果照樣沒有我們希冀的1000,上面我們剖析一下緣由

在 java 渣滓收受接管整頓一文中,描寫了jvm運轉時辰內存的分派。個中有一個內存區域是jvm虛擬機棧,每個線程運轉時都有一個線程棧,線程棧保留了線程運轉時刻變量值信息。當線程拜訪某一個對象時刻值的時刻,起首經由過程對象的援用找到對應在堆內存的變量的值,然後把堆內存變量的詳細值load到線程當地內存中,樹立一個變量正本,以後線程就不再和對象在堆內存變量值有任何干系,而是直接修正正本變量的值,在修正完以後的某一個時辰(線程加入之前),主動把線程變量正本的值回寫到對象在堆中變量。如許在堆中的對象的值就發生變更了。上面一幅圖

描寫這寫交互

java volatile1

read and load 從主存復制變量到以後任務內存
use and assign  履行代碼,轉變同享變量值
store and write 用任務內存數據刷新主存相干內容

個中use and assign 可以屢次湧現

然則這一些操作其實不是原子性,也就是 在read load以後,假如主內存count變量產生修正以後,線程任務內存中的值因為曾經加載,不會發生對應的變更,所以盤算出來的成果會和預期紛歧樣

關於volatile潤飾的變量,jvm虛擬機只是包管從主內存加載到線程任務內存的值是最新的

例如假設線程1,線程2 在停止read,load 操作中,發明主內存中count的值都是5,那末都邑加載這個最新的值

在線程1堆count停止修正以後,會write到主內存中,主內存中的count變量就會變成6

線程2因為曾經停止read,load操作,在停止運算以後,也會更新主內存count的變量值為6

招致兩個線程實時用volatile症結字修正以後,照樣會存在並發的情形。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved