1、安全發佈對象的發佈與逃逸。
發佈對象,使一個對象能夠被當前範圍之外的代碼所使用。
對象逸出,一種錯誤的發佈,當一個對象還沒有構造完成時,就使它被其他線程所見。
如果不正確的發佈了可變對象,會造成兩種錯誤,首先是發佈線程以外的任何線程都可以看到被髮布對象的過期的值。其次呢,線程看到的被髮布對象的引用是最新的,然而呢,被髮布對象的狀態卻是過期的,如果一個對象是可變對象,那麼它就要被安全發佈纔可以。
2、安全發佈對象的四種方式。
1)、第一種,在靜態初始化函數中初始化一個對象引用。
2)、第二種,將對象的引用保存到volatile類型域或者AtomicReference對象中。
3)、第三種,將對象的引用保存到某個正確構造對象的final類型域中。
4)、第四種,將對象的引用保存到一個由鎖保護的域中。
2.1、懶漢模式 ,單例實例在第一次使用時進行創建,單線程運行沒有問題的,但是多線程可能在getInstance出現線程不安全的情況。
1 package com.bie.concurrency.example.singleton; 2 3 import com.bie.concurrency.annoations.NotThreadSafe; 4 5 /** 6 * 7 * 8 * @Title: SingletonExample1.java 9 * @Package com.bie.concurrency.example.singleton 10 * @Description: TODO 11 * @author biehl 12 * @date 2020年1月6日 13 * @version V1.0 14 * 15 * 1、懶漢模式 ,單例實例在第一次使用時進行創建 16 * 17 * 2、單線程運行沒有問題的,但是多線程可能在getInstance出現線程不安全的情況 18 * 19 * 3、第一種,在靜態初始化函數中初始化一個對象引用。 20 * 21 */ 22 @NotThreadSafe 23 public class SingletonExample1 { 24 25 // 私有的默認構造方法,避免外部通過new創建對象。 26 private SingletonExample1() { 27 } 28 29 // 定義單例對象,至少保證有一個對象被創建的。 30 private static SingletonExample1 singletonExample1 = null; 31 32 // 靜態工廠方法 33 public static SingletonExample1 getInstance() { 34 // 判斷對象是否爲null和創建對象,此處導致了線程不安全 35 if (null == singletonExample1) { 36 singletonExample1 = new SingletonExample1(); 37 } 38 return singletonExample1; 39 } 40 41 }
2.2、餓漢模式 ,單例實例在類裝載時進行創建,餓漢模式是線程安全的。餓漢模式,如果單例類構造方法中沒有過多的操作處理,餓漢模式是可以接受的。餓漢模式的缺點,如果單例類構造方法中存在過多的操作處理,會導致該類加載的過慢。可能會引起性能問題。
1 package com.bie.concurrency.example.singleton; 2 3 import javax.annotation.concurrent.ThreadSafe; 4 5 /** 6 * 7 * 8 * @Title: SingletonExample1.java 9 * @Package com.bie.concurrency.example.singleton 10 * @Description: TODO 11 * @author biehl 12 * @date 2020年1月6日 13 * @version V1.0 14 * 15 * 1、餓漢模式 ,單例實例在類裝載時進行創建,餓漢模式是線程安全的。 16 * 17 * 2、餓漢模式,如果單例類構造方法中沒有過多的操作處理,餓漢模式是可以接受的。 18 * 19 * 3、餓漢模式的缺點,如果單例類構造方法中存在過多的操作處理,會導致該類加載的過慢。可能會引起性能問題。 20 * 21 * 4、第一種,在靜態初始化函數中初始化一個對象引用。 22 * 23 */ 24 @ThreadSafe 25 public class SingletonExample2 { 26 27 // 私有的默認構造方法,避免外部通過new創建對象。 28 // 餓漢模式,私有構造方法沒有過多處理。餓漢模式創建的對象肯定會在實際中被使用,不會造成資源浪費。 29 private SingletonExample2() { 30 } 31 32 // 定義單例對象,至少保證有一個對象被創建的。在類裝載的時候進行創建保證了線程的安全性。 33 private static SingletonExample2 singletonExample2 = new SingletonExample2(); 34 35 // 靜態工廠方法 36 public static SingletonExample2 getInstance() { 37 return singletonExample2; 38 } 39 40 }
2.3、懶漢模式 ,單例實例在第一次使用時進行創建,方法加synchronized修飾,不推薦,雖然保證了線程安全性,但是帶來了性能方面的開銷。
1 package com.bie.concurrency.example.singleton; 2 3 import com.bie.concurrency.annoations.NotRecommend; 4 import com.bie.concurrency.annoations.ThreadSafe; 5 6 /** 7 * 8 * 9 * @Title: SingletonExample1.java 10 * @Package com.bie.concurrency.example.singleton 11 * @Description: TODO 12 * @author biehl 13 * @date 2020年1月6日 14 * @version V1.0 15 * 16 * 1、懶漢模式 ,單例實例在第一次使用時進行創建 17 * 18 * 2、方法加synchronized修飾,不推薦,雖然保證了線程安全性,但是帶來了性能方面的開銷。 19 * 20 * 3、第四種,將對象的引用保存到一個由鎖保護的域中。 21 */ 22 @ThreadSafe 23 @NotRecommend 24 public class SingletonExample3 { 25 26 // 私有的默認構造方法,避免外部通過new創建對象。 27 private SingletonExample3() { 28 } 29 30 // 定義單例對象,至少保證有一個對象被創建的。 31 private static SingletonExample3 singletonExample3 = null; 32 33 // 靜態工廠方法 34 // 使用synchronized修飾,方法內部所有實現同一時間內只能由一個線程訪問。 35 // 因此可以保證線程安全的。 36 public static synchronized SingletonExample3 getInstance() { 37 if (null == singletonExample3) { 38 singletonExample3 = new SingletonExample3(); 39 } 40 return singletonExample3; 41 } 42 43 }
2.4、懶漢模式,【雙重同步鎖單例模式 】,單例實例在第一次使用時進行創建,此實現是,線程不安全的,JVM和cpu優化,發生了指令重排。
1 package com.bie.concurrency.example.singleton; 2 3 import com.bie.concurrency.annoations.NotThreadSafe; 4 5 /** 6 * 7 * 8 * @Title: SingletonExample1.java 9 * @Package com.bie.concurrency.example.singleton 10 * @Description: TODO 11 * @author biehl 12 * @date 2020年1月6日 13 * @version V1.0 14 * 15 * 1、懶漢模式,【雙重同步鎖單例模式 】,單例實例在第一次使用時進行創建 16 * 17 * 2、此實現是,線程不安全的,JVM和cpu優化,發生了指令重排 18 * 19 */ 20 @NotThreadSafe 21 public class SingletonExample4 { 22 23 // 1、memory = allocate() 分配對象的內存空間 24 // 2、ctorInstance() 初始化對象 25 // 3、instance = memory 設置instance指向剛分配的內存 26 27 // JVM和cpu優化,發生了指令重排 28 29 // 1、memory = allocate() 分配對象的內存空間 30 // 3、instance = memory 設置instance指向剛分配的內存 31 // 2、ctorInstance() 初始化對象 32 33 // 私有的默認構造方法,避免外部通過new創建對象。 34 private SingletonExample4() { 35 } 36 37 // 定義單例對象,至少保證有一個對象被創建的。 38 private static SingletonExample4 singletonExample4 = null; 39 40 // 靜態工廠方法 41 public static SingletonExample4 getInstance() { 42 // 雙重檢測機制 43 if (singletonExample4 == null) { 44 // 同步鎖,判斷對象不爲空以後,鎖着SingletonExample4類 45 // synchronized修飾的內部,同一時間只能由一個線程可以訪問的。 46 synchronized (SingletonExample4.class) { 47 // 再次進行判斷,如果singletonExample4爲空,就進行創建對象。 48 if (singletonExample4 == null) { 49 singletonExample4 = new SingletonExample4(); 50 } 51 } 52 } 53 return singletonExample4; 54 } 55 56 }
2.5、懶漢模式,【雙重同步鎖單例模式 】,單例實例在第一次使用時進行創建,此實現是,線程安全的,volatile禁止指令重排序。
1 package com.bie.concurrency.example.singleton; 2 3 import com.bie.concurrency.annoations.ThreadSafe; 4 5 /** 6 * 7 * 8 * @Title: SingletonExample1.java 9 * @Package com.bie.concurrency.example.singleton 10 * @Description: TODO 11 * @author biehl 12 * @date 2020年1月6日 13 * @version V1.0 14 * 15 * 1、懶漢模式,【雙重同步鎖單例模式 】,單例實例在第一次使用時進行創建 16 * 17 * 2、此實現是,線程安全的,volatile禁止指令重排序。 18 * 19 * 3、第二種,將對象的引用保存到volatile類型域或者AtomicReference對象中。 20 * 21 */ 22 @ThreadSafe 23 public class SingletonExample5 { 24 25 // 1、memory = allocate() 分配對象的內存空間 26 // 2、ctorInstance() 初始化對象 27 // 3、instance = memory 設置instance指向剛分配的內存 28 29 // 私有的默認構造方法,避免外部通過new創建對象。 30 private SingletonExample5() { 31 } 32 33 // 定義單例對象,至少保證有一個對象被創建的。 34 // 單例對象 volatile + 雙重檢測機制 -> 禁止指令重排 35 // volatile適用場景做狀態標識量、雙重檢測,此處就是volatile的雙重檢測使用場景。 36 private volatile static SingletonExample5 singletonExample4 = null; 37 38 // 靜態工廠方法 39 public static SingletonExample5 getInstance() { 40 // 雙重檢測機制 41 if (singletonExample4 == null) { 42 // 同步鎖,判斷對象不爲空以後,鎖着SingletonExample4類 43 // synchronized修飾的內部,同一時間只能由一個線程可以訪問的。 44 synchronized (SingletonExample5.class) { 45 // 再次進行判斷,如果singletonExample4爲空,就進行創建對象。 46 if (singletonExample4 == null) { 47 singletonExample4 = new SingletonExample5(); 48 } 49 } 50 } 51 return singletonExample4; 52 } 53 54 }
2.6、餓漢模式 ,單例實例在類裝載時進行創建,餓漢模式是線程安全的。
1 package com.bie.concurrency.example.singleton; 2 3 import javax.annotation.concurrent.ThreadSafe; 4 5 /** 6 * 7 * 8 * @Title: SingletonExample1.java 9 * @Package com.bie.concurrency.example.singleton 10 * @Description: TODO 11 * @author biehl 12 * @date 2020年1月6日 13 * @version V1.0 14 * 15 * 1、餓漢模式 ,單例實例在類裝載時進行創建,餓漢模式是線程安全的。 16 * 17 * 18 */ 19 @ThreadSafe 20 public class SingletonExample6 { 21 22 // 私有的默認構造方法,避免外部通過new創建對象。 23 // 餓漢模式,私有構造方法沒有過多處理。餓漢模式創建的對象肯定會在實際中被使用,不會造成資源浪費。 24 private SingletonExample6() { 25 } 26 27 // 定義單例對象,至少保證有一個對象被創建的。在類裝載的時候進行創建保證了線程的安全性。 28 private static SingletonExample6 singletonExample6 = null; 29 30 // 靜態塊初始化對象singletonExample6 31 static { 32 singletonExample6 = new SingletonExample6(); 33 } 34 35 // 靜態工廠方法 36 public static SingletonExample6 getInstance() { 37 return singletonExample6; 38 } 39 40 public static void main(String[] args) { 41 System.out.println(getInstance().hashCode()); 42 System.out.println(getInstance().hashCode()); 43 } 44 45 }
2.7、枚舉方式。線程安全,推薦的方式。相比於懶漢模式,在安全性方面更容易保證,在餓漢模式,在安全性方面,在實際調用方面纔可以初始化,不會造成資源的浪費。
1 package com.bie.concurrency.example.singleton; 2 3 import javax.annotation.concurrent.ThreadSafe; 4 5 import com.bie.concurrency.annoations.Recommend; 6 7 /** 8 * 9 * 10 * @Title: SingletonExample1.java 11 * @Package com.bie.concurrency.example.singleton 12 * @Description: TODO 13 * @author biehl 14 * @date 2020年1月6日 15 * @version V1.0 16 * 17 * 1、枚舉方式。線程安全,推薦的方式。 18 * 19 * 2、相比於懶漢模式,在安全性方面更容易保證,在餓漢模式,在安全性方面,在實際調用方面纔可以初始化,不會造成資源的浪費。 20 * 21 */ 22 @ThreadSafe 23 @Recommend 24 public class SingletonExample7 { 25 26 // 私有的默認構造方法,避免外部通過new創建對象。 27 private SingletonExample7() { 28 } 29 30 // 靜態工廠方法 31 public static SingletonExample7 getInstance() { 32 return Singleton.INSTANCE.getInstance(); 33 } 34 35 // 枚舉類,私有的枚舉類。 36 private enum Singleton { 37 // instance 38 INSTANCE; 39 40 // 私有的類的實例 41 private SingletonExample7 singletonExample7; 42 43 // JVM保證這個方法絕對只調用一次 44 // 枚舉類的構造方法 45 Singleton() { 46 singletonExample7 = new SingletonExample7(); 47 } 48 49 // 提供一個方法方便類來獲取 50 public SingletonExample7 getInstance() { 51 // 返回枚舉類裏面的實例 52 return singletonExample7; 53 } 54 } 55 56 }
作者:別先生
博客園:https://www.cnblogs.com/biehongli/
如果您想及時得到個人撰寫文章以及著作的消息推送,可以掃描上方二維碼,關注個人公衆號哦。