<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-67  評論-522  文章-0  trackbacks-0
        在Java并發編程里面,volatile是個很重要的概念,大象也來講講自己對它的理解。
        以前曾經有段時間我一直沒搞明白volatile到底怎么用,它是怎樣實現的同步,而且對于volatile變量還有一些限制條件。任何技術在沒完全弄明白之前,至少在沒熟練掌握之前都不太敢放心大膽的用,大象想將自己對它的理解分享出來,給需要的人一些幫助。
        volatile是輕量級的鎖,它只具備可見性,但沒有原子特性。用volatile聲明的變量,它的同步特性,簡單來講就是對該變量的單個讀/寫是同步的。這是什么意思呢?我還是以共享變量i為例,不過在i的前面加上了volatile修飾符。
        private volatile int i = 0;
        public int get() {
            return i;
        }
        public void set(int i) {
            this.i = i;
        }

        為了與傳統的getXXXsetXXX方法區別開來,我將方法名改成了上面這樣。對于getset方法,如果有多個線程同時訪問,volatile是可以保證i的原子性的,再簡單點講,對于變量igetset方法是同步的。這是通過什么來保證的呢?是通過Java語言規范:如果一個字段被聲明為volatileJava內存模型確保所有線程看到這個變量的值是一致的。
        看到這里,可能有的童鞋會想,既然volatile可以保證內存可見性,那不就解決了淺談Java共享變量這篇文章里面講到的共享變量的并發問題嗎?只要在i的前面加上volatile就可以解決同步問題了,你確定?實踐是最好的辦法,動手試下,看看結果如何。
        事實證明這個辦法行不通,為什么呢?原因出在i++上面,增量操作符++不是原子的。這個操作分解開來看是先從堆內存中獲得i值的副本放到緩存中,然后對副本值加1,最后再將副本值寫回到堆內存的變量i中。從這個過程我們可以看到,從堆內存中獲得i(get方法)以及將值寫回到i(set方法)這兩步都是同步的,但中間的就不能保證是同步的了。
        對于volatile修飾的變量,只保證了他的可見性,但不保證原子性。最常用的應該是boolean類型,它用來作為狀態標志,因為它只有truefalse兩個值,不會有非原子性的操作。當然不是說只能用在布爾類型變量上面,其它的基本類型和對象類型都可以用。但一定需要小心謹慎的處理,以免掉進并發陷阱而不知。比如volatile變量就不適合用于不變性條件這種情況,以上下限為例,lower必須小于upper,這就是一種不變性條件,你可以理解為這是一種規則限制。它們都只有setget方法,但在set方法里面加入了約束條件,這時,volatile的可見性就不能保證并發時,lowerupper之間的不變性條件(lower<upper)一定成立了。

        /*
         * volatile只保證lowerupper的最后寫入一定會被其它讀取的線程看到
         * 但不能保證在lowerupper寫入時,另一個變量的值沒有發生變化
         */
        private volatile int lower;
        private volatile int upper;

        public int getLower() {
            return lower;
        }

        public int getUpper() {
            return upper;
        }

        public void setLower(int lower) {
            if (lower > upper)
                throw new IllegalArgumentException();
            this.lower = lower;
        }

        public void setUpper(int upper) {
            if (upper < lower)
                throw new IllegalArgumentException();
            this.upper = upper;
        }

        如果lowerupper的初始值為010,同一時刻,線程1調用setLower(8),線程2調用setUpper(2),執行上完全沒問題,但是現在的lowerupper的值就變為了82這種無效的數據了,所以volatile只能確保可見性,不能確保原子性。
        所以在使用volatile變量時,請考慮是否滿足下面這樣的要求:
            1、對變量的寫入操作不依賴變量的當前值(i++這種操作就不行)
            2、沒有用于其它變量的不變式條件中(lower<upper)
        到這里,我們已經明白了:用volatile修飾的變量只具備可見性,那么它是怎么保證可見性的呢?
        現在大家用的電腦CPU基本上都是多核的,至少兩核,緩存也有很多級(L1L2L3)。代碼在JVM里面執行的時候,JVM如果發現有CPU在處理volatile變量的寫入操作,就會告訴該CPU將當前緩存中的數據寫回到堆內存中,但這時其它CPU的緩存值還是舊的,再執行操作就會有問題,所以在處理器的內部實現了緩存一致性協議,當有緩存中的數據寫回內存時會引起其它CPU里這個volatile變量的緩存值無效,如果這時候其它CPU要想使用就必須到堆內存中重新讀取該值,這樣就實現了volatile變量的可見性。我這樣講方便大家理解,實際的情況比這復雜的多。
        以上是大象關于volatile變量的一些淺薄見解,真的很淺,大象學藝不精,有什么不對的,還請各位指出來。謝謝!
        本文為菠蘿大象原創,如要轉載請注明出處。http://www.tkk7.com/bolo
    posted on 2014-06-20 17:08 菠蘿大象 閱讀(5979) 評論(2)  編輯  收藏 所屬分類: Concurrency

    評論:
    # re: 淺談volatile變量的理解[未登錄] 2014-06-22 21:20 | 星情
    比 ibm devloper works 上的那篇關于 volatitle 的文章更易于理解  回復  更多評論
      
    # re: 淺談volatile變量的理解 2014-07-18 19:58 | zhangchao
    簡單來說,volatile就是告訴程序,該變量是易變的,不穩定的,每次必須去主存讀取,而不要從自己的緩存中獲取副本。  回復  更多評論
      
    主站蜘蛛池模板: 在线亚洲v日韩v| 亚洲免费一级视频| 日韩亚洲AV无码一区二区不卡| 84pao强力永久免费高清| 亚洲永久在线观看| 亚洲成a人片在线观看国产| 久久免费视频精品| 亚洲精品无AMM毛片| 亚洲成色999久久网站| 在线看片人成视频免费无遮挡| 国产精品九九久久免费视频| 亚洲国产成人精品无码区在线秒播 | 我的小后妈韩剧在线看免费高清版| 亚洲A∨精品一区二区三区下载| 亚洲色爱图小说专区| 免费被黄网站在观看| a在线观看免费视频| 亚洲日韩一区精品射精| 亚洲AV综合色区无码另类小说| 日韩免费一区二区三区| 24小时在线免费视频| 精品无码一级毛片免费视频观看 | 成人黄网站片免费视频| WWW亚洲色大成网络.COM| 亚洲美女自拍视频| 国产亚洲精品福利在线无卡一| 成人人观看的免费毛片| 人妻无码久久一区二区三区免费| 国产偷国产偷亚洲高清人| 亚洲人成网站日本片| 亚洲av成人无码久久精品| 亚洲日本va午夜中文字幕久久| 中文字幕av无码无卡免费| 少妇人妻偷人精品免费视频| 九九九国产精品成人免费视频| 亚洲色大情网站www| 91亚洲自偷在线观看国产馆| 亚洲精品私拍国产福利在线| 亚洲一级特黄大片无码毛片| 免费无码又爽又刺激高潮的视频| 国产国产人免费视频成69堂|