單例模式的特點
- 一個類只允許產生一個實例化對象。
- 單例類構造方法私有化,不允許外部創建對象。
- 單例類向外提供靜態方法,調用方法返回內部創建的實例化對象。
懶漢式(線程不安全)
其主要表現在單例類在外部需要創建實例化對象時再進行實例化,進而達到lazy loading 的效果。
通過靜態方法 getsingleton() 和private 權限構造方法為創建一個實例化對象提供唯一的途徑。
不足:未考慮到多線程的情況下可能會存在多個訪問者同時訪問,發生構造出多個對象的問題,所以在多線程下不可用這種方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/** * @author mrroot * @since 2018-12-17 * 懶漢式(線程不安全) */ public class singleton { private static singleton singleton; private singleton(){ } public static singleton singleton(){ if (singleton == null ){ singleton = new singleton(); } return singleton; } } |
懶漢式(線程安全,同步方法,不推薦使用)
針對懶漢式的線程不安全,自然會想到給 getsingleton() 進行 synchronized 加鎖來保證線程同步。
不足:效率低。大多數情況下這個鎖占用的額外資源都浪費了,每個線程在想獲得類的實例時候,執行 getsingleton() 方法都要進行同步。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/** * @author mrroot * @since 2019-3-27 * 懶漢式(線程安全,同步方法,不推薦使用) */ public class singleton { private static singleton singleton; private singleton(){ } public static synchronized singleton singleton(){ if (singleton == null ){ singleton = new singleton(); } return singleton; } } |
餓漢式(線程安全)
在進行類加載時完成實例化對象的過程就是餓漢式的形式。
避免了線程同步問題,在運行這個類的時候進行加載,之后直接訪問
不足:相比接下來的靜態內部類而言,這種方法比靜態內部類多了內存常駐,容易造成內存浪費,也未達到延遲加載的效果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/** * @author mrroot * @since 2019-3-27 * 餓漢式(線程安全) */ public class singleton{ private static singleton singleton = new singleton(); private singleton(){ } public static singleton singleton(){ return singleton; } } |
靜態內部類加載(線程安全)
靜態內部類不會在單例加載時加載,當調用 getsingleton() 方法時才會進行加載,達到類似懶漢式效果,并且也是線程安全的。
類的靜態屬性只會在第一次加載類時進行初始化,所以上面的方法jvm 幫助我們保證了線程的安全性,在類進行初始化時,其他線程無法進入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/** * @author mrroot * @since 2019-3-27 * 靜態內部類加載(線程安全) */ public class singleton{ private static singleton singleton; private static class singletoninner{ private static final singleton instance = new singleton(); } public static singleton getsingleton(){ return singletoninner.instance; } } |
枚舉(線程安全)
自由串行化;保證只有一個實例;線程安全。
effective java 作者所提倡的方法,近乎完美,在繼承場景下不適用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/** * @author mrroot * @since 2019-3-27 * 枚舉(線程安全) */ enum singleton{ instance; public void method(){ } } class test{ public static void main(string[] args) { singleton.instance.method(); } } |
懶漢式雙重校驗鎖法(通常線程安全,不可保證完全安全)
使用同步代碼塊避免了第二種方法的效率低的問題,但此方法并不能完全起到線程同步的作用,與上面第一種方法產生的問題相似,多線程訪問時可能產生多個對象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
/** * @author mrroot * @since 2019-3-27 * 懶漢式雙重校驗鎖法(通常線程安全,不可保證完全安全) */ class singleton{ private static singleton singleton; private singleton(){ } public static singleton singleton(){ if (singleton == null ){ synchronized (singleton. class ){ if (singleton == null ){ singleton = new singleton(); } } } return singleton; } } |
懶漢式雙重檢查終極版
與第六種方法不同的是,此方法給singleton 的聲明上加了關鍵字 volatile ,進而解決了低概率的線程不安全問題。
volatile 起到禁止指令重排的作用,在它賦值完成之前,就不會調用讀操作(singleton == null)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
/** * @author mrroot * @since 2019-3-27 * 懶漢式雙重檢查終極版(volatile) */ class singleton{ private static volatile singleton singleton; private singleton(){ } public static singleton singleton(){ if (singleton == null ){ synchronized (singleton. class ){ if (singleton == null ){ singleton = new singleton(); } } } return singleton; } } |
使用 threadlocal 實現(線程安全)
threadlocal 會為每一個線程提供一個獨立的變量副本,從而隔離了多個線程對數據的訪問沖突。
對于多線程資源共享的問題,同步機制采用了“以時間換空間”的方式,而threadlocal 采用了“以空間換時間”的方式。前者僅提供一份變量,讓不同的線程排隊訪問,而后者為每一個線程都提供了一份變量,因此可以同時訪問而互不影響。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/** * @author mrroot * @since 2019-3-27 * 使用 threadlocal 實現(線程安全) */ class singleton{ private static final threadlocal<singleton> singleton = new threadlocal<singleton>(){ @override protected singleton initialvalue(){ return new singleton(); } }; private singleton(){ } public static singleton getsingleton(){ return singleton.get(); } } |
使用cas 鎖實現(線程安全)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
/** * @author mrroot * @since 2019-3-27 * 使用 cas 實現(線程安全) */ public class singleton { private static final atomicreference<singleton> instance = new atomicreference<singleton>(); private singleton(){ } public static final singleton getsingleton(){ for (;;){ singleton current = instance.get(); if (current != null ){ return current; } current = new singleton(); if (instance.compareandset( null ,current)){ return current; } } } public static void main(string[] args) { singleton singleton1 = singleton.getsingleton(); singleton singleton2 = singleton.getsingleton(); system.out.println(singleton1 == singleton2); } } |
以上所述是小編給大家介紹的9種java單例模式詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!
原文鏈接:https://www.cnblogs.com/mrroot/p/10606356.html