JMM和happens-before原则
vJMM:
Java Memory Model(Java內存模型),圍繞著在并發過程中如何處理可見性、原子性、有序性這三個特性而建立的模型。
v可見性:
JMM提供了volatile變量定義、final、synchronized塊來保證可見性。
例如:線程a在將共享變量x=1寫入主內存的時候,如何保證線程b讀取共享變量x的值為1,這就是JMM做的事情。JMM通過控制主內存與每個線程的本地內存之間的交互,來為java程序員提供內存可見性保證。
v原子性:
JMM提供保證了訪問基本數據類型的原子性(其實在寫一個工作內存變量到主內存是分主要兩步:store、write),但是實際業務處理場景往往是需要更大的范圍的原子性保證,所以模型也提供了synchronized塊來保證。
v有序性:
這個概念是相對而言的,如果在本線程內,所有的操作都是有序的,如果在一個線程觀察另一個線程,所有的操作都是無序的,前句是“線程內表現為串行行為”,后句是“指令的重排序”和“工作內存和主內存同步延遲”現象,模型提供了volatile和synchronized來保證線程之間操作的有序性。
v重排序:
在執行程序時為了提高性能,編譯器和處理器常常會對指令做重排序(編譯器、處理器),就是因為這些重排序,所以可能會導致多線程程序出現內存可見性問題(數據安全問題)和有序性問題。
JMM是如何處理的呢?
對于編譯器,JMM的編譯器重排序規則會禁止特定類型的編譯器重排序
對于處理器重排序,JMM的處理器重排序規則會要求java編譯器在生成指令序列時,插入特定類型的內存屏障(memory barriers,intel稱之為memory fence)指令,通過內存屏障指令來禁止特定類型的處理器重排序
總之一句話,JMM是通過禁止特定類型的編譯器重排序和處理器重排序來為程序員提供一致的內存可見性保證。
A線程具體什么時候刷新共享數據到主內存是不確定的,假設我們使用了同步原語(synchronized,volatile和final),那么刷新的時間是確定的,例如:線程A釋放鎖后會同步到主內存,線程B獲取鎖后會同步主內存數據,即“A線程釋放鎖--B線程獲取鎖”可以實現A,B線程之間的通信。
?
vjava中volatile關鍵字的含義
http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html
vJava內存模型happens-before法則
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.?
----------------------------
v什么是happens-before?
happens-before就是“什么什么一定在什么什么之前運行”,也就是保證順序性。?
因為CPU是可以不按我們寫代碼的順序執行內存的存取過程的,也就是指令會亂序或并行運行,?
只有上面的happens-before所規定的情況下,才保證順序性。
v為什么存在可見性問題?
簡單介紹下。相對于內存,CPU的速度是極高的,如果CPU需要存取數據時都直接與內存打交道,在存取過程中,CPU將一直空閑,這是一種極大的浪費,媽媽說,浪費是不好的,所以,現代的CPU里都有很多寄存器,多級cache,他們比內存的存取速度高多了。某個線程執行時,內存中的一份數據,會存在于該線程的工作存儲中(working memory,是cache和寄存器的一個抽象,這個解釋源于《Concurrent Programming in Java: Design Principles and Patterns, Second Edition》§2.2.7,原文:Every thread is defined to have a working memory (an abstraction of caches and registers) in which to store values. 有不少人覺得working memory是內存的某個部分,這可能是有些譯作將working memory譯為工作內存的緣故,為避免混淆,這里稱其為工作存儲,每個線程都有自己的工作存儲),并在某個特定時候回寫到內存。單線程時,這沒有問題,如果是多線程要同時訪問同一個變量呢?內存中一個變量會存在于多個工作存儲中,線程1修改了變量a的值什么時候對線程2可見?此外,編譯器或運行時為了效率可以在允許的時候對指令進行重排序,重排序后的執行順序就與代碼不一致了,這樣線程2讀取某個變量的時候線程1可能還沒有進行寫入操作呢,雖然代碼順序上寫操作是在前面的。這就是可見性問題的由來。
并且,多個CPU之間的緩存也不保證實時同步,?
也就是說你剛給一個變量賦值,另一個線程立即獲取它的值,可能拿到的卻是舊值(或null),?
因為兩個線程在不同的CPU執行,它們看到的緩存值不一樣,?
只有在synchronized或volatile或final的性況下才能保證正確性,?
很多人用synchronized時只記得有lock的功能,而忘記了線程間的可見性問題。?
check()中的 n != n 好像永遠不會成立,因為他們指向同一個值,但非同步時卻很有可能發生。?
另外,JMM不保證創建過程的原子性,讀寫并發時,可能看到不完整的對象,?
這也是為什么單例模式中著名的"雙重檢查成例"方法,在Java中行不通。(但.Net的內存模型保證這一點)?
當然,在Java中單例的延遲加載可以用另一種方案實現(方案四):?
v方案一:非延遲加載單例類?
public class Singleton {private Singleton(){}private static final Singleton instance = new Singleton();public static Singleton getInstance() {return instance; } }v方案二:簡單的同步延遲加載?
public class Singleton { private static Singleton instance = null;public static synchronized Singleton getInstance() {if (instance == null)instance = new Singleton();return instance; } }v方案三:雙重檢查成例延遲加載?
目的是避開過多的同步,?
但在Java中行不通,因為同步塊外面的if (instance == null)可能看到已存在,但不完整的實例。?
JDK5.0以后版本若instance為volatile則可行?
用volatile修飾的變量,線程在每次使用變量的時候,都會讀取變量修改后的最新的值。volatile很容易被誤用,用來進行原子性操作。
public class Singleton { private static Singleton instance = null;public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance; } }v方案四:類加載器延遲加載?
public class Singleton { private static class Holder {static final Singleton instance = new Singleton();}public static Singleton getInstance() {return Holder.instance; } }本文轉自 小眼兒 博客園博客,原文鏈接:http://www.cnblogs.com/hujunzheng/p/5118256.html,如需轉載請自行聯系原作者
總結
以上是生活随笔為你收集整理的JMM和happens-before原则的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hadoop/Spark生态圈里的新气象
- 下一篇: 10.23cron10.24chkcon