<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>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 12 什么?還有這種操作!—有序性 > 理想必須要人們去實現它,它不但需要決心和勇敢而且需要知識。 > ——吳玉章 前面我們學習了原子性和可見性。相比較而言,可見性更難理解一點,但是由于緩存已經在日常編程中大量被使用,我們并不陌生,所以理解起來也沒什么難度。不過本節要講的有序性,我們之前并沒有接觸過相關的知識,理解起來會比較抽象。 ## 1\. 什么是有序性 有序性指的是代碼在運行期間保證按照編寫的順序。這句話看起來和可見性的定義一樣,好像又是一句廢話。你一定在想,代碼當然是按照編寫順序執行的,否則那還不亂套了?其實并不是這樣,代碼執行的順序還真不一定和你編寫的順序一致。多線程開發復雜就復雜在和我們的認知相違背,我們如果在做多線程開發前不一一搞清楚,那么所編寫出的并發代碼一定是漏洞百出。 ## 2\. 指令重排序 說到有序性,我們一定會提到指令重排序。CPU 為了提高運行效率,可能會對編譯后代碼的指令做一些優化,這些優化不能保證 100% 符合你編寫代碼在正常編譯后的順序執行。但是一定能保證代碼執行的結果和按照編寫順序執行的結果是一致的。 指令重排序并不是毫無約束的隨意改變代碼執行順序,而是需要符合指令間的依賴關系,否則會造成程序執行結果錯誤。 我們接下來通過一個例子來理解指令重排序的必要性。 星期六早上,你要去超市進行采購,你自己想買兩斤小龍蝦,你兒子和你說要一袋巧克力,然后你老婆說家里沒有醬油了買一瓶,你媽又說買兩根胡蘿卜。那么你到了超市會死板的按照小龍蝦、巧克力、醬油、胡蘿卜的順序去采購嗎?當然不會,你肯定會大致規劃好路線,從離超市入口最近的貨架開始采購,避免走回頭路。不管你采購的順序如何,最終你肯定會保證所有人給你的需求全部實現。CPU 也是如此,雖然是機器,但它也會規劃更為合理的執行方式,確保程序運行正確的情況下,提高效率。 ![圖片描述](https://img.mukewang.com/5d91656a0001e26911240587.jpg) 我們再來看一個不能重排序的例子。還是去超市采購,你媽和你說,如果買不到西葫蘆,才買胡蘿卜。那么買西葫蘆和胡蘿卜這兩個步驟就不能改變。否則假如我們先去了胡蘿卜貨架,發現自己沒買到西葫蘆,就會買胡蘿卜,然后又執行了買西葫蘆。最后的結果就是錯誤的 ---- 我們既買了西葫蘆也買了胡蘿卜。 指令重排序的優化,僅僅對單線程程序確保安全。如果在并發的情況下,程序沒能保證有序性,程序的執行結果往往會出乎我們的意料。另外注意,指令重排序,并不是代碼重排序。我們的代碼被編譯后,一行代碼可能會對應多條指令,所以指令重排序更為細粒度。 ## 3\. 單例實現遇到的有序性問題 我們在實現單例的時候,有一種方式叫做雙重判斷。首先判斷 instance 是不是為空,如果為空進入同步代碼塊初始化 instance,否而直接返回 instance。初始化 instance 時再次判斷 instance 是否為空,避免了在進入同步代碼塊這段時間有線程搶先一步完成了 instance 初始化。代碼如下: ~~~java public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getSingleton() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } ~~~ 這種單例的實現方式,看似在提高效率的同時,做到了天衣無縫。其實不然,因為 instance = new Singleton (); 這一行代碼會被編譯為三條指令,正常指令順序如下: 1. 為 instance 分配一塊內存 A 2. 在分配的內存 A 上初始化 instance 實例 3. 把內存 A 的地址賦值給 instance 變量 而編譯器優化后可能會變成: 1. 為 instance 分配一塊內存 A 2. 把內存 A 的地址賦值給 instance 變量 3. 在分配的內存 A 上初始化 instance 實例 ![圖片描述](https://img.mukewang.com/5d9c6f330001d47016000919.jpg) 可以看出在優化后第 2 和第 3 步調換了位置。調換后單線程運行是沒有問題的。但是換做多線程,假如線程 A 正在初始化 instance,此時執行完第 2 步,正在執行第三步。而線程 B 執行到 if (instance == null) 的判斷,那么線程 B 就會直接得到未初始化好的 instance,而此時線程 B 使用此 instance 顯然是有問題的。 要解決本例的有序性問題很簡單,我們只需要為 instance 聲明時增加 volatile 關鍵字,volatile 修飾的變量是會保證讀操作一定能讀到寫完的值。 ## 總結 有序性是在多線程的情況下,確保 CPU 不對我們需要保證順序性的代碼進行重排序的。我們可以通過 sychronized 或者 volatile 來確保有序性。至此,關于多線程的三大特性已經學習完成。我們在多線程開發過程中要牢記原子性、可見性、有序性。千萬不要以寫單線程程序的思路來開發多線程,處理好這三大特性,多線程開發的大部分問題都會得以解決。下一節我們會來學習 Java 內存模型,其實所有的線程安全性都來自于它。
                  <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>

                              哎呀哎呀视频在线观看