序言
在23種設(shè)計模式中,單例是最簡單的設(shè)計模式,但是也是很常用的設(shè)計模式。從單例的五種實現(xiàn)方式中我們可以看到程序員對性能的不懈追求。下面我將分析單例的五種實現(xiàn)方式的優(yōu)缺點,并對其在多線程環(huán)境下的性能進行測試。
實現(xiàn)
單例模式適用于資源占用較多的類,保證一個類只有一個實例即單例。通用的做法就是構(gòu)造器私有化,提供一個全局的訪問點,返回類的實例。
uml圖:
1.餓漢式
代碼實現(xiàn):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package com.zgh.gof23.singleton; /** * 餓漢式 * @author yuelin * */ public class SingleDemo { private static SingleDemo instance = new SingleDemo(); //私有化構(gòu)造器 private SingleDemo() { //防止其他通過反射調(diào)用構(gòu)造方法,破解單例 if (instance != null ) { throw new RuntimeException(); } } //對外提供統(tǒng)一的訪問點 public static SingleDemo getInstance() { return instance; } } |
優(yōu)點
1.實例的初始化由JVM裝載類的時候進行,保證了線程的安全性
2.實現(xiàn)簡單方便
3.實例的訪問效率高
缺點
1.不能實現(xiàn)懶加載,如果不調(diào)用getInstance(),那么這個類就白白的占據(jù)內(nèi)存,資源的利用率不高
注意
1.防止通過反射調(diào)用構(gòu)造方法破解單例模式。
2.防止通過反序列產(chǎn)生新的對象。
2.懶漢式
代碼實現(xiàn):
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
|
package com.zgh.gof23.singleton; /** * 懶漢式實現(xiàn)單例 * * @author zhuguohui * */ public class SingleDemo2 { // 此處并不初始化實例 private static SingleDemo2 instance; private SingleDemo2() { if (instance != null ) { throw new RuntimeException(); } } /** * 當調(diào)用此方法的時候才初始化實例, 為了實現(xiàn)線程安全,需要使用同步方法 * * @return */ public static synchronized SingleDemo2 getInstance() { if (instance == null ) { instance = new SingleDemo2(); } return instance; } } |
優(yōu)點
1.只有使用這個類的時候才初始化實例,優(yōu)化了資源利用率
缺點
1.為了實現(xiàn)線程安全,使用了同步方法獲取,增加了訪問的開銷
注意
1.防止通過反射調(diào)用構(gòu)造方法破解單例模式。
2.防止通過反序列產(chǎn)生新的對象。
3.雙重檢查
代碼實現(xiàn):
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
|
package com.zgh.gof23.singleton; /** * 雙重檢查 * * @author zhuguohui * */ public class SingleDemo3 { private static SingleDemo3 instance; private SingleDemo3() { if (instance != null ) { throw new RuntimeException(); } } public static SingleDemo3 getInstance() { //第一重檢查,提高效率 if (instance == null ) { synchronized (SingleDemo3. class ) { //第二重檢查保證線程安全 if (instance == null ) { instance = new SingleDemo3(); } } } return instance; } } |
優(yōu)點
1.實現(xiàn)懶加載
2.通過縮小同步區(qū)域和第一次檢查提高訪問效率
缺點
1.為了實現(xiàn)線程安全,使用了同步方法獲取,增加了訪問的開銷
注意
1.防止通過反射調(diào)用構(gòu)造方法破解單例模式。
2.防止通過反序列產(chǎn)生新的對象。
4.靜態(tài)內(nèi)部類
代碼實現(xiàn):
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
|
/** * 靜態(tài)內(nèi)部類實現(xiàn)單例 * * @author zhuguohui * */ public class SingleDemo4 { private static SingleDemo4 instance; private static class SingleDemo4Holder { private static final SingleDemo4 instance = new SingleDemo4(); } private SingleDemo4() { if (instance != null ) { throw new RuntimeException(); } } /** * 調(diào)用這個方法的時候,JVM才加載靜態(tài)內(nèi)部類,才初始化靜態(tài)內(nèi)部類的類變量。由于由JVM初始化,保證了線程安全性, * 同時又實現(xiàn)了懶加載 * @return */ public static SingleDemo4 getInstance() { return SingleDemo4Holder.instance; } } |
優(yōu)點
1.即實現(xiàn)了線程安全,又實現(xiàn)了懶加載
缺點
2.實現(xiàn)稍顯復雜
5.枚舉實現(xiàn)
代碼實現(xiàn):
1
2
3
4
5
6
7
8
9
|
/** * 枚舉實現(xiàn)單例 * 枚舉由JVM實現(xiàn)其的單例性 * @author zhuguohui * */ public enum SingleDemo5 { INSTANCE; } |
優(yōu)點
1.實現(xiàn)簡單
2.線程安全
3.天熱對反射和反序列化漏洞免疫(由JVM提供)
缺點
2.不能實現(xiàn)懶加載
注意
1.防止通過反射調(diào)用構(gòu)造方法破解單例模式。
2.防止通過反序列產(chǎn)生新的對象。
測試
源碼
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
|
public class APP { public static void main(String[] args) { int threadCount = 100 ; long start = System.currentTimeMillis(); final CountLock lock = new CountLock(threadCount); for ( int i = 0 ; i < threadCount; i++) { new Thread( new Runnable() { @Override public void run() { for ( int j = 0 ; j < 10000000 ; j++) { //通過更換此處,來測試不同單例實現(xiàn)方式在多線程環(huán)境下的性能 SingleDemo5 demo = SingleDemo5.INSTANCE; } lock.finish(); } }).start(); } //等待所有線程執(zhí)行完 lock.waitForWrok(); long end = System.currentTimeMillis(); System.out.println( "總共耗時" + (end - start)); } } |
為了統(tǒng)計所以線程執(zhí)行完需要的時間,我寫了一個工具類
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
32
33
34
|
package com.zgh.gof23.singleton; public class CountLock { //線程的總數(shù)量 private int count; public CountLock( int count) { this .count = count; } /** * 當一個線程完成任務以后,調(diào)用一次這個方法 */ public synchronized void finish() { count--; if (count == 0 ) { notifyAll(); } } /** * 需要等待其他線程執(zhí)行完的線程,調(diào)用此方法。 */ public synchronized void waitForWrok() { while (count > 0 ) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } |
結(jié)果
五種單例實現(xiàn)方式,在100個線程下,每個線程訪問1千萬次實例的用時.
Tables | 實現(xiàn)方式 | 用時(毫秒) |
---|---|---|
1 | 餓漢式 | 13 |
2 | 懶漢式 | 10778 |
3 | 雙重檢查 | 15 |
4 | 靜態(tài)內(nèi)部類 | 14 |
5 | 枚舉 | 12 |
(*注意:由于不同電腦之間的性能差異,測試的結(jié)果可能不同)
總結(jié)
如果需要懶加載就使用靜態(tài)內(nèi)部類方式,如果不需要就使用枚舉方式。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
原文鏈接:http://blog.csdn.net/qq_22706515/article/details/74202814