java 單例的五種實現(xiàn)方式及其性能分析
java 單例的五種實現(xiàn)方式及其性能分析
序言
在23種設(shè)計模式中,單例是最簡單的設(shè)計模式,但是也是很常用的設(shè)計模式。從單例的五種實現(xiàn)方式中我們可以看到程序員對性能的不懈追求。下面我將分析單例的五種實現(xiàn)方式的優(yōu)缺點,并對其在多線程環(huán)境下的性能進(jìn)行測試。
實現(xiàn)
單例模式適用于資源占用較多的類,保證一個類只有一個實例即單例。通用的做法就是構(gòu)造器私有化,提供一個全局的訪問點,返回類的實例。
uml圖:

1.餓漢式
代碼實現(xiàn):
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裝載類的時候進(jìn)行,保證了線程的安全性
2.實現(xiàn)簡單方便
3.實例的訪問效率高
缺點
1.不能實現(xiàn)懶加載,如果不調(diào)用getInstance(),那么這個類就白白的占據(jù)內(nèi)存,資源的利用率不高
注意
1.防止通過反射調(diào)用構(gòu)造方法破解單例模式。
2.防止通過反序列產(chǎn)生新的對象。
2.懶漢式
代碼實現(xiàn):
package com.zgh.gof23.singleton;
/**
* 懶漢式實現(xiàn)單例
*
* @author zhuguohui
*
*/
public class SingleDemo2 {
// 此處并不初始化實例
private static SingleDemo2 instance;
private SingleDemo2() {
if (instance != null) {
throw new RuntimeException();
}
}
/**
* 當(dāng)調(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):
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):
/**
* 靜態(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)稍顯復(fù)雜
5.枚舉實現(xiàn)
代碼實現(xiàn):
/**
* 枚舉實現(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)生新的對象。
測試
源碼
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í)行完需要的時間,我寫了一個工具類
package com.zgh.gof23.singleton;
public class CountLock {
//線程的總數(shù)量
private int count;
public CountLock(int count) {
this.count = count;
}
/**
* 當(dāng)一個線程完成任務(wù)以后,調(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)部類方式,如果不需要就使用枚舉方式。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關(guān)文章
SpringSecurity?認(rèn)證實現(xiàn)流程分析
SpringSecurity框架通過一個過濾器鏈來處理認(rèn)證和授權(quán),主要包括UsernamePasswordAuthenticationFilter負(fù)責(zé)處理登錄請求,本文給大家介紹SpringSecurity?認(rèn)證實現(xiàn)登錄校驗,感興趣的朋友跟隨小編一起看看吧2024-10-10
Java設(shè)計模式之中介者模式(Mediator Pattern)簡介
這篇文章主要介紹了Java設(shè)計模式之中介者模式(Mediator Pattern),需要的朋友可以參考下2014-07-07
javafx實現(xiàn)圖片3D翻轉(zhuǎn)效果方法實例
程序?qū)崿F(xiàn)思路: 在javafx中Node對象有一個effect屬性,可以用于實現(xiàn)各種特效。PerspectiveTransform特效可以使Node對象實現(xiàn)透視變換。因此我們可以通過計算透視變換中每個點的位置來實現(xiàn)3D翻轉(zhuǎn)特效。2013-04-04
解決springboot項目啟動報錯Error creating bean with&nb
這篇文章主要介紹了解決springboot項目啟動報錯Error creating bean with name dataSourceScriptDatabaseInitializer問題,具有很好的參考價值,希望對大家有所幫助2024-03-03
Spring Security 將用戶數(shù)據(jù)存入數(shù)據(jù)庫
這篇文章主要介紹了Spring Security 如何將用戶數(shù)據(jù)存入數(shù)據(jù)庫,幫助大家更好的理解和學(xué)習(xí)Spring Security,感興趣的朋友可以了解下2020-09-09
一文詳解Java中的動態(tài)填充Html模版并轉(zhuǎn)PDF
在后端技術(shù)中,模板引擎和PDF生成工具是兩個非常重要的領(lǐng)域,Thymeleaf和wkhtmltopdf是這兩個領(lǐng)域的杰出代表,下面就來詳細(xì)介紹一下Thymeleaf和wkhtmltopdf的技術(shù)特點吧2023-12-12

