java 單例的五種實(shí)現(xiàn)方式及其性能分析
java 單例的五種實(shí)現(xiàn)方式及其性能分析
序言
在23種設(shè)計(jì)模式中,單例是最簡(jiǎn)單的設(shè)計(jì)模式,但是也是很常用的設(shè)計(jì)模式。從單例的五種實(shí)現(xiàn)方式中我們可以看到程序員對(duì)性能的不懈追求。下面我將分析單例的五種實(shí)現(xiàn)方式的優(yōu)缺點(diǎn),并對(duì)其在多線程環(huán)境下的性能進(jìn)行測(cè)試。
實(shí)現(xiàn)
單例模式適用于資源占用較多的類(lèi),保證一個(gè)類(lèi)只有一個(gè)實(shí)例即單例。通用的做法就是構(gòu)造器私有化,提供一個(gè)全局的訪問(wèn)點(diǎn),返回類(lèi)的實(shí)例。
uml圖:
1.餓漢式
代碼實(shí)現(xiàn):
package com.zgh.gof23.singleton; /** * 餓漢式 * @author yuelin * */ public class SingleDemo { private static SingleDemo instance = new SingleDemo(); //私有化構(gòu)造器 private SingleDemo() { //防止其他通過(guò)反射調(diào)用構(gòu)造方法,破解單例 if (instance != null) { throw new RuntimeException(); } } //對(duì)外提供統(tǒng)一的訪問(wèn)點(diǎn) public static SingleDemo getInstance() { return instance; } }
優(yōu)點(diǎn)
1.實(shí)例的初始化由JVM裝載類(lèi)的時(shí)候進(jìn)行,保證了線程的安全性
2.實(shí)現(xiàn)簡(jiǎn)單方便
3.實(shí)例的訪問(wèn)效率高
缺點(diǎn)
1.不能實(shí)現(xiàn)懶加載,如果不調(diào)用getInstance(),那么這個(gè)類(lèi)就白白的占據(jù)內(nèi)存,資源的利用率不高
注意
1.防止通過(guò)反射調(diào)用構(gòu)造方法破解單例模式。
2.防止通過(guò)反序列產(chǎn)生新的對(duì)象。
2.懶漢式
代碼實(shí)現(xiàn):
package com.zgh.gof23.singleton; /** * 懶漢式實(shí)現(xiàn)單例 * * @author zhuguohui * */ public class SingleDemo2 { // 此處并不初始化實(shí)例 private static SingleDemo2 instance; private SingleDemo2() { if (instance != null) { throw new RuntimeException(); } } /** * 當(dāng)調(diào)用此方法的時(shí)候才初始化實(shí)例, 為了實(shí)現(xiàn)線程安全,需要使用同步方法 * * @return */ public static synchronized SingleDemo2 getInstance() { if (instance == null) { instance = new SingleDemo2(); } return instance; } }
優(yōu)點(diǎn)
1.只有使用這個(gè)類(lèi)的時(shí)候才初始化實(shí)例,優(yōu)化了資源利用率
缺點(diǎn)
1.為了實(shí)現(xiàn)線程安全,使用了同步方法獲取,增加了訪問(wèn)的開(kāi)銷(xiāo)
注意
1.防止通過(guò)反射調(diào)用構(gòu)造方法破解單例模式。
2.防止通過(guò)反序列產(chǎn)生新的對(duì)象。
3.雙重檢查
代碼實(shí)現(xiàn):
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)點(diǎn)
1.實(shí)現(xiàn)懶加載
2.通過(guò)縮小同步區(qū)域和第一次檢查提高訪問(wèn)效率
缺點(diǎn)
1.為了實(shí)現(xiàn)線程安全,使用了同步方法獲取,增加了訪問(wèn)的開(kāi)銷(xiāo)
注意
1.防止通過(guò)反射調(diào)用構(gòu)造方法破解單例模式。
2.防止通過(guò)反序列產(chǎn)生新的對(duì)象。
4.靜態(tài)內(nèi)部類(lèi)
代碼實(shí)現(xiàn):
/** * 靜態(tài)內(nèi)部類(lèi)實(shí)現(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)用這個(gè)方法的時(shí)候,JVM才加載靜態(tài)內(nèi)部類(lèi),才初始化靜態(tài)內(nèi)部類(lèi)的類(lèi)變量。由于由JVM初始化,保證了線程安全性, * 同時(shí)又實(shí)現(xiàn)了懶加載 * @return */ public static SingleDemo4 getInstance() { return SingleDemo4Holder.instance; } }
優(yōu)點(diǎn)
1.即實(shí)現(xiàn)了線程安全,又實(shí)現(xiàn)了懶加載
缺點(diǎn)
2.實(shí)現(xiàn)稍顯復(fù)雜
5.枚舉實(shí)現(xiàn)
代碼實(shí)現(xiàn):
/** * 枚舉實(shí)現(xiàn)單例 * 枚舉由JVM實(shí)現(xiàn)其的單例性 * @author zhuguohui * */ public enum SingleDemo5 { INSTANCE; }
優(yōu)點(diǎn)
1.實(shí)現(xiàn)簡(jiǎn)單
2.線程安全
3.天熱對(duì)反射和反序列化漏洞免疫(由JVM提供)
缺點(diǎn)
2.不能實(shí)現(xiàn)懶加載
注意
1.防止通過(guò)反射調(diào)用構(gòu)造方法破解單例模式。
2.防止通過(guò)反序列產(chǎn)生新的對(duì)象。
測(cè)試
源碼
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++) { //通過(guò)更換此處,來(lái)測(cè)試不同單例實(shí)現(xiàn)方式在多線程環(huán)境下的性能 SingleDemo5 demo = SingleDemo5.INSTANCE; } lock.finish(); } }).start(); } //等待所有線程執(zhí)行完 lock.waitForWrok(); long end = System.currentTimeMillis(); System.out.println("總共耗時(shí)" + (end - start)); } }
為了統(tǒng)計(jì)所以線程執(zhí)行完需要的時(shí)間,我寫(xiě)了一個(gè)工具類(lèi)
package com.zgh.gof23.singleton; public class CountLock { //線程的總數(shù)量 private int count; public CountLock(int count) { this.count = count; } /** * 當(dāng)一個(gè)線程完成任務(wù)以后,調(diào)用一次這個(gè)方法 */ 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é)果
五種單例實(shí)現(xiàn)方式,在100個(gè)線程下,每個(gè)線程訪問(wèn)1千萬(wàn)次實(shí)例的用時(shí).
Tables | 實(shí)現(xiàn)方式 | 用時(shí)(毫秒) |
---|---|---|
1 | 餓漢式 | 13 |
2 | 懶漢式 | 10778 |
3 | 雙重檢查 | 15 |
4 | 靜態(tài)內(nèi)部類(lèi) | 14 |
5 | 枚舉 | 12 |
(*注意:由于不同電腦之間的性能差異,測(cè)試的結(jié)果可能不同)
總結(jié)
如果需要懶加載就使用靜態(tài)內(nèi)部類(lèi)方式,如果不需要就使用枚舉方式。
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
SpringBoot全局異常與數(shù)據(jù)校驗(yàn)的方法
這篇文章主要介紹了SpringBoot全局異常與數(shù)據(jù)校驗(yàn)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11Spring Boot Gradle發(fā)布war到tomcat的方法示例
本篇文章主要介紹了Spring Boot Gradle發(fā)布war到tomcat的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03使用Java將字節(jié)數(shù)組轉(zhuǎn)成16進(jìn)制形式的代碼實(shí)現(xiàn)
在很多場(chǎng)景下,需要進(jìn)行分析字節(jié)數(shù)據(jù),但是我們存起來(lái)的字節(jié)數(shù)據(jù)一般都是二進(jìn)制的,這時(shí)候就需要我們將其轉(zhuǎn)成16進(jìn)制的方式方便分析,本文主要介紹如何使用Java將字節(jié)數(shù)組格式化成16進(jìn)制的格式并輸出,需要的朋友可以參考下2024-05-05使用IntelliJ IDEA2020.2.2 x64 新建java項(xiàng)目并且輸出Hello World
這篇文章主要介紹了使用IntelliJ IDEA2020.2.2 x64 新建java項(xiàng)目并且輸出Hello World,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11Springboot+Redis實(shí)現(xiàn)API接口防刷限流的項(xiàng)目實(shí)踐
本文主要介紹了Springboot+Redis實(shí)現(xiàn)API接口防刷限流的項(xiàng)目實(shí)踐,通過(guò)限流可以讓系統(tǒng)維持在一個(gè)相對(duì)穩(wěn)定的狀態(tài),為更多的客戶提供服務(wù),具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07