Java併發編程與高併發之安全發佈對象

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/

如果您想及時得到個人撰寫文章以及著作的消息推送,可以掃描上方二維碼,關注個人公衆號哦。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章