posts - 12, comments - 8, trackbacks - 0, articles - 5
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          [轉]JAVA內(nèi)存模型:happens-before法則

          Posted on 2010-11-18 14:42 楊羅羅 閱讀(1092) 評論(0)  編輯  收藏 所屬分類: java.thread
          JMM規(guī)范: 

          The rules for happens-before are: 


          Program order rule. Each action in a thread happens-before every action in that thread that comes later in the program order. 



          Monitor lock rule. An unlock on a monitor lock happens-before every subsequent lock on that same monitor lock. 



          Volatile variable rule. A write to a volatile field happens-before every subsequent read of that same field. 



          Thread start rule. A call to Thread.start on a thread happens-before every action in the started thread. 



          Thread termination rule. Any action in a thread happens-before any other thread detects that thread has terminated, either by successfully return from Thread.join or by Thread.isAlive returning false. 



          Interruption rule. A thread calling interrupt on another thread happens-before the interrupted thread detects the interrupt (either by having InterruptedException tHRown, or invoking isInterrupted or interrupted). 



          Finalizer rule. The end of a constructor for an object happens-before the start of the finalizer for that object. 



          Transitivity. If A happens-before B, and B happens-before C, then A happens-before C. 


          appens-before完整規(guī)則:

          (1)同一個線程中的每個Action都happens-before于出現(xiàn)在其后的任何一個Action。

          (2)對一個監(jiān)視器的解鎖happens-before于每一個后續(xù)對同一個監(jiān)視器的加鎖。

          (3)對volatile字段的寫入操作happens-before于每一個后續(xù)的同一個字段的讀操作。

          (4)Thread.start()的調(diào)用會happens-before于啟動線程里面的動作。

          (5)Thread中的所有動作都happens-before于其他線程檢查到此線程結束或者Thread.join()中返回或者Thread.isAlive()==false。

          (6)一個線程A調(diào)用另一個另一個線程B的interrupt()都happens-before于線程A發(fā)現(xiàn)B被A中斷(B拋出異常或者A檢測到B的isInterrupted()或者interrupted())。

          (7)一個對象構造函數(shù)的結束happens-before與該對象的finalizer的開始

          (8)如果A動作happens-before于B動作,而B動作happens-before與C動作,那么A動作happens-before于C動作。



          ---------------------------- 


          什么是happens-before? 
          happens-before就是“什么什么一定在什么什么之前運行”,也就是保證順序性。 
          因為CPU是可以不按我們寫代碼的順序執(zhí)行內(nèi)存的存取過程的,也就是指令會亂序或并行運行, 
          只有上面的happens-before所規(guī)定的情況下,才保證順序性。 
          如: 
          Java代碼 
          1. public class Test {  
          2.   
          3.     private int a = 0;  
          4.   
          5.     private long b = 0;  
          6.   
          7.     public void set() {  
          8.         a = 1;  
          9.         b = -1;  
          10.     }  
          11.   
          12.     public void check() {  
          13.         if (! ((b == 0) || (b == -1 && a == 1))  
          14.             throw new Exception("check Error!");  
          15.     }  
          16. }  

          對于set()方法的執(zhí)行: 
          1. 編譯器可以重新安排語句的執(zhí)行順序,這樣b就可以在a之前賦值。如果方法是內(nèi)嵌的(inline),編譯器還可以把其它語句重新排序。 
          2. 處理器可以改變這些語句的機器指令的執(zhí)行順序,甚到同時執(zhí)行這些語句。 
          3. 存儲系統(tǒng)(由于被緩存控制單元控制)也可以重新安排對應存儲單元的寫操作順序,這些寫操作可能與其他計算和存儲操作同時發(fā)生。 
          4. 編譯器,處理器和存儲系統(tǒng)都可以把這兩條語句的機器指令交叉執(zhí)行。 
          例如:在一臺32位的機器上,可以先寫b的高位,然后寫a,最后寫b的低位,(注:b為long類型,在32位的機器上分高低位存儲) 
          5. 編譯器,處理器和存儲系統(tǒng)都可以使對應于變量的存儲單元一直保留著原來的值, 
          以某種方式維護相應的值(例如,在CPU的寄存器中)以保證代碼正常運行,直到下一個check調(diào)用才更新。 
          ... 
          在單線程(或同步)的情況下,上面的check()永遠不會報錯, 
          但非同步多線程運行時卻很有可能。 


          并且,多個CPU之間的緩存也不保證實時同步, 
          也就是說你剛給一個變量賦值,另一個線程立即獲取它的值,可能拿到的卻是舊值(或null), 
          因為兩個線程在不同的CPU執(zhí)行,它們看到的緩存值不一樣, 
          只有在synchronized或volatile或final的性況下才能保證正確性, 
          很多人用synchronized時只記得有l(wèi)ock的功能,而忘記了線程間的可見性問題。 
          如: 
          Java代碼 
          1. public class Test {  
          2.   
          3.     private int n;  
          4.   
          5.     public void set(int n) {  
          6.         this.n = n;  
          7.     }  
          8.   
          9.     public void check() {  
          10.         if (n != n)  
          11.             throw new Exception("check Error!");  
          12.     }  
          13. }  

          check()中的 n != n 好像永遠不會成立,因為他們指向同一個值,但非同步時卻很有可能發(fā)生。 

          另外,JMM不保證創(chuàng)建過程的原子性,讀寫并發(fā)時,可能看到不完整的對象, 
          這也是為什么單例模式中著名的"雙重檢查成例"方法,在Java中行不通。(但.Net的內(nèi)存模型保證這一點) 
          當然,在Java中單例的延遲加載可以用另一種方案實現(xiàn)(方案四): 

          方案一:非延遲加載單例類 
          Java代碼 
          1. public class Singleton {  
          2.   
          3.   private Singleton(){}  
          4.   
          5.   private static final Singleton instance = new Singleton();  
          6.   
          7.   public static Singleton getInstance() {  
          8.     return instance;     
          9.   }   
          10. }  


          方案二:簡單的同步延遲加載 
          Java代碼 
          1. public class Singleton {   
          2.   
          3.   private static Singleton instance = null;  
          4.   
          5.   public static synchronized Singleton getInstance() {  
          6.     if (instance == null)  
          7.       instance = new Singleton();  
          8.     return instance;     
          9.   }   
          10.   
          11. }   


          方案三:雙重檢查成例延遲加載 
          目的是避開過多的同步, 
          但在Java中行不通,因為同步塊外面的if (instance == null)可能看到已存在,但不完整的實例。 
          JDK5.0以后版本若instance為volatile則可行 
          Java代碼 
          1. public class Singleton {   
          2.   
          3.   private static Singleton instance = null;  
          4.   
          5.   public static Singleton getInstance() {  
          6.     if (instance == null) {  
          7.         synchronized (Singleton.class) {  
          8.             if (instance == null) {  
          9.                 instance = new Singleton();  
          10.             }  
          11.         }  
          12.     }  
          13.     return instance;     
          14.   }   
          15.   
          16. }   


          方案四:類加載器延遲加載 
          Java代碼 
          1. public class Singleton {   
          2.   
          3.   private static class Holder {  
          4.     static final Singleton instance = new Singleton();  
          5.   }  
          6.   
          7.   public static Singleton getInstance() {  
          8.     return Holder.instance;     
          9.   }   
          10.   
          11. }   

          主站蜘蛛池模板: 察隅县| 兰西县| 滦南县| 清水河县| 新巴尔虎右旗| 简阳市| 永寿县| 宾阳县| 南澳县| 常德市| 永兴县| 郧西县| 茶陵县| 南川市| 烟台市| 公主岭市| 九龙城区| 邵阳市| 阿瓦提县| 宝清县| 个旧市| 陵川县| 惠安县| 天峻县| 浑源县| 平舆县| 清远市| 景泰县| 车险| 哈尔滨市| 仙游县| 天门市| 金堂县| 卫辉市| 邓州市| 贵溪市| 浑源县| 和硕县| 资兴市| 台州市| 盐池县|