1、什麼是內存模型,爲何要使用它緩存
若是缺乏同步,那麼將會有許多因素使得線程沒法當即甚至永遠看到一個線程的操做結果安全
在單線程中,只要程序的最終結果與在嚴格串行環境中執行的結果相同,那麼上述全部操做都是容許的多線程
在多線程中,JVM經過同步操做來找出這些協調操做將在什麼時候發生架構
JMM規定了JVM必須遵循一組最小保證,這組保證規定了對變量的寫入操做在什麼時候將對其餘線程可見app
一、平臺的內存模型函數
每一個處理器都擁有本身的緩存,而且按期地與主內存進行協調,在不一樣的處理器架構中提供了不一樣級別的緩存一致性,即容許不一樣的處理器在任意時刻從同一個存儲位置上看到不一樣的值。JVM經過在適當的位置上插入內存柵欄來屏蔽在JMM與底層平臺內存模型之間的差別。Java程序不須要指定內存柵欄的位置,而只需經過正確地使用同步來找出什麼時候將訪問共享狀態性能
二、重排序線程
各類使操做延遲或者看似亂序執行的不一樣緣由,均可以歸爲重排序,內存級的重排序會使程序的行爲變得不可預測code
1 Thread one = new Thread(new Runnable() { 2 public void run() { 3 a = 1; 4 x = b; 5 } 6 });
上述代碼也會有如下結果:對象
三、Java內存模式簡介
Java內存模型是經過各類操做來定義的,包括變量的讀/寫操做,監視器的加鎖和釋放操做,以及線程的啓動和合並操做
JMM爲程序中全部的操做定義了一個偏序關係,稱爲Happens-Before,使在正確同步的程序中不存在數據競爭(缺少Happens-Before關係,那麼JVM能夠對它們任意地重排序)
四、藉助同步
」藉助(Piggyback)「現有同步機制的可見性屬性,對某個未被鎖保護的變量的訪問操做進行排序(不但願給對象加鎖,而又想維護它的順序)
Happens-Before排序包括:
2、發佈
形成不正確發佈的真正緣由:"發佈一個共享對象"與"另外一個線程訪問該對象"之間缺乏一種Happens-Before的關係
一、不安全的發佈
除了不可變對象之外,使用被另外一個線程初始化的對象一般都是不安全的,除非對象的發佈操做是在使用該對象的線程開始使用以前執行
1 public class UnsafeLazyInitialization { 2 private static Object resource; 3 4 public static Object getInstance(){ 5 if (resource == null){ 6 resource = new Object(); //不安全的發佈 7 } 8 return resource; 9 } 10 }
緣由一:線程B看到了線程A發佈了一半的對象
緣由二:即便線程A初始化Resource實例以後再將resource設置爲指向它,線程B仍可能看到對resource的寫入操做將在對Resource各個域的寫入操做以前發生。由於線程B看到的線程A中的操做順序,可能與線程A執行這些操做時的順序並不相同
二、安全發佈
例:BlockingQueue的同步機制保證put在take後執行,A線程放入對象能保證B線程取出時是安全的
藉助於類庫中如今的同步容器、使用鎖保護共享變量、或都使用共享的volatile類型變量,均可以保證對該變量的讀取和寫入是按照happens-before排序的
happens-before事實上能夠比安全發佈承諾更強的可見性與排序性
三、安全初始化模式
方式一:加鎖保證可見性與排序性,存在性能問題
1 public class UnsafeLazyInitialization { 2 private static Object resource; 3 4 public synchronized static Object getInstance(){ 5 if (resource == null){ 6 resource = new Object(); //不安全的發佈 7 } 8 return resource; 9 } 10 }
方式二:提早初始化,可能形成浪費資源
1 public class EagerInitialization { 2 private static Object resource = new Object(); 3 public static Object getInstance(){ 4 return resource; 5 } 6 }
方式三:延遲初始化,建議
1 public class ResourceFactory { 2 private static class ResourceHolder{ 3 public static Object resource = new Object(); 4 } 5 public static Object getInstance(){ 6 return ResourceHolder.resource; 7 } 8 }
方式四:雙重加鎖機制,注意保證volatile類型,不然出現一致性問題
1 public class DoubleCheckedLocking { 2 private static volatile Object resource; 3 public static Object getInstance(){ 4 if (resource == null){ 5 synchronized (DoubleCheckedLocking.class){ 6 if (resource == null){ 7 resource = new Object(); 8 } 9 } 10 } 11 return resource; 12 } 13 }
3、初始化過程當中的安全性
初始化安全性只能保證經過final域可達的值從構造過程完成時可見性。對於經過非final域可達的值,或者在構成過程完成後可能改變的值,必須採用同步來確保可見性