<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # Java關鍵字volatile ## 提綱 ![](https://img.kancloud.cn/d0/97/d0972ed8bc45ec59cba59eb308055754_1614x1100.png) ## 定義 語義上,volatile是表示易變的、不確定的。 功能上,是Java提供的最輕量級的同步機制。 ## 前因:從CPU緩存架構類比JMM線程工作內存和主內存關系 要弄懂如何保證可見性的,請看下圖,左側是CPU的緩存架構圖: ![](https://img.kancloud.cn/65/e5/65e5262acbf3bfdc13933e143d43576d_1492x1014.png) 如下圖是一些時間參考,可更加直觀的感受到各個組件的訪問速度。 ![](https://img.kancloud.cn/2f/d4/2fd4896988e5dfaba560b28d80246287_644x410.png) 因為CPU的執行速度和內存的讀寫速度,相差太大。 CPU完成操作后,如果要等到內存也執行完成再繼續下一個操作的話,對CPU算力就是極大的浪費。所以為了匹配2者的速度差,引入了高速緩存。現在CPU一般都有3級緩存,其中一級緩存離CPU核最近,速度也最快,可分為指令緩存和數據緩存2部分;下面是二級緩存,一個CPU核心就配備一個一級緩存和二級緩存的,是私有的。而三級緩存則是共享的,再下面是數據總線和主內存。 如下圖是CPU的基本信息: ![](https://img.kancloud.cn/6e/40/6e40417b3a3113e1e8cf720cb2d0118a_870x858.png) 引入了高速緩存,雖然能讓CPU效率提升,但是也帶來了緩存一致性問題。為了解決這個問題,有兩種方案,一是通過總線鎖實現強一致性;二是緩存一致性協議,目前大多數采用的是MESI緩存一致性協議。(這2個方案都是硬件級別的) **隨著CPU技術的發展,在CPU硬件級別多是使用的第二種方式,因為鎖住總線期間,其他CPU無法訪問內存,導致性能下降** 而對于Java并發環境下,多線程的共享數據一致性問題也是類似,Java內存模型參考上述的CPU緩存架構實現了自己的線程、工作內存和主內存的關系,如上圖里的右側部分。 ### 總線鎖 所謂總線鎖就是使用處理器提供的一個LOCK#信號,當一個處理器在總線上輸出此信號時,其他處理器的請求將被阻塞住,那么該處理器可以獨占共享內存,從而保證操作的原子性。 ### 緩存一致性協議MESI MESI協議是當前最主流的緩存一致性協議,在MESI協議中,每個緩存行有4個狀態,可用2個bit表示,它們分別是: ``` Modified(修改):數據有效,數據被修改了,和內存中數據不一致,數據只存在于本Cache中。 Exclusive(獨享):數據有效,數據和內存中的數據一致,數據只存在于本Cache中。 Shared(共享):數據有效,數據和內存中的數據一致,數據存在多個Cache中。 Invalid(無效):數據無效,一旦數據被標記為無效,那效果就等同于它從來沒被加載到緩存中。 ``` 其詳細狀態轉換如下: ![](https://img.kancloud.cn/de/b5/deb5659392a4b0093b82fc02ce18dfb4_872x1697.png) ## 特性 因為Java內存模型對volatile關鍵字的支持,使得volatile修飾的變量(實例字段、靜態變量或者數組對象的元素,不包含局部變量,因為局部變量是線程私有的)具備了如下特性: * **多線程間的可見性** * **有序性,禁止指令重排序** * **不保證原子性,如volatile int i=1;i++;** ## volatile底層實現原理 ### volatile修飾的底層區別 首先通過如下一段DCL(double check lock)程序來比對一下有volatile和沒有volatile修飾變量的在匯編指令上的區別: ~~~ public class VolatileSingleton { public static volatile VolatileSingleton instance; public static VolatileSingleton getInstance(){ if(instance == null){ synchronized(VolatileSingleton.class){ if(instance== null){ instance = new VolatileSingleton(); } } } return instance; } public static void main(String[] args) { VolatileSingleton.getInstance(); } } ~~~ 通過加上如下虛擬機參數,可以只顯示getInstance()方法的匯編指令: ``` # server模式運行 -server # 讓虛擬機編譯模式執行代碼 -Xcomp # 使用hsdis來顯示執行的匯編指令,不同平臺的hsdis插件請自行查閱安裝 -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly # 如下2個命令,只打印關心部分的匯編指令,如果不指定會打印很多其他方法的匯編,造成混亂 # 編譯命令,不要內聯編譯getInstance方法 -XX:CompileCommand=dontinline,*VolatileSingleton.getInstance # 編譯命令,只編譯getInstance方法 -XX:CompileCommand=compileonly,*VolatileSingleton.getInstance ``` 最后將沒有加volatile修飾的匯編指令保存到novolatile.txt,加了volatile的保存到volatile.txt,再使用idea的compare with 對比如下圖: ![](https://img.kancloud.cn/ba/9f/ba9f0efe3d199c045164ee8f30fd39e4_2960x1572.png) 會發現加了volatile的會多出一行 **lock addl $0x0,(%rsp)** 的匯編指令,這個指令是一個內存屏障。 指令`lock addl $0x0,(%esp)`是一個空操作,關鍵在于 lock 前綴,查詢 IA32 手冊,它的作用是使得本 CPU 的 Cache 寫入了內存,該寫入動作也會引起別的 CPU invalidate 其 Cache。所以通過這樣一個空操作,可讓前面 volatile 變量的修改對其他 CPU 立即可見。 ### volatile基于軟內存屏障實現可見性和有序性 ![](https://img.kancloud.cn/8c/12/8c1288f4d4ac3550ae016c72ed8fe13b_1600x1298.png) 通過內存屏障指令lock,如果有修改,處理器會將該變量所在緩存行的數據會寫到主內存,并使得其他CPU里該變量所在的緩存行失效,從而保證該變量的可見性。 而且內存屏障會保證后面的指令不會重排序到屏障前面,從而保證有序性。 #### 可見性定義 對于共享變量a,當線程1修改a的值后,其他線程能立即知道這個修改,就說變量a對所有線程有可見性。 #### 可見性例子 ~~~ /** * volatile 可見性測試 */ public class VolatileVisibilityTest { private static volatile boolean ready; private static int number; private static class ReaderThread extends Thread{ @Override public void run() { while (!ready); System.out.println(number); } } public static void main(String[] args) throws InterruptedException { new ReaderThread().start(); Thread.sleep(1000); number = 42; ready = true; Thread.sleep(10000); } } /** * 因為JMM保證了volatile變量ready的可見性,在main線程中修改為true; * ReaderThread線程能應用到這個修改,則while(!ready)循環得以跳過。 * 則輸出42,,10秒后退出程序。如果ready沒有修飾為volatile,則沒有可見性,線程Reader會陷入死循環,程序永遠不會停止。 */ ~~~ ### volatile不保證原子性 比如復雜操作,i++; ~~~ /** * volatile不保證原子性 * @Author: mango * @Date: 2022/7/4 11:32 下午 */ public class VolatileNoAtomicTest { private static volatile int number = 0; static class AdderThread extends Thread{ @Override public void run() { for(int i=0;i<10000;i++){ number++; } } } public static void main(String[] args) throws InterruptedException { Thread t1 = new AdderThread(); t1.start(); Thread t2 = new AdderThread(); t2.start(); t1.join(); t2.join(); System.out.println(number); } } /** * 結果: * 有時候輸出小于20000的值,說明number++無法保證原子性 */ ~~~ ## volatile優化 追加volatile變量的寬度為操作系統緩存行的寬度,一般為64字節。Java中對象的引用是4字節,`LinkedTransferQueue`會在每個入隊元素的對象引用后填充60個字節,將元素補齊到64字節來提升并發下的入隊和出隊效率。使用追加到64字節的方式來填滿高速緩沖區的緩存行,避免頭接點和尾節點加載到同一個緩存行,使得頭尾節點在修改時不會互相鎖定。 ### 不需要補齊到64字節的場景 1. 系統的緩存行不是64字節的,有的是32字節。 2. 共享變量不會被頻繁的寫。 ## 參考文檔 * 書籍:葛一鳴 *《Java高并發程序設計第二版》 * 網上文章:https://www.cnblogs.com/zhangxl1016/articles/16001715.html * 網上文章:https://blog.csdn.net/stackfuture/article/details/122252734 * 網上文章:https://www.cnblogs.com/hbbbs/articles/12116286.html
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看