溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java 單例的五種實現方式及其性能分析

發(fā)布時間:2020-09-18 02:54:17 來源:腳本之家 閱讀:152 作者:-SOLO- 欄目:編程語言

java 單例的五種實現方式及其性能分析

序言

在23種設計模式中,單例是最簡單的設計模式,但是也是很常用的設計模式。從單例的五種實現方式中我們可以看到程序員對性能的不懈追求。下面我將分析單例的五種實現方式的優(yōu)缺點,并對其在多線程環(huán)境下的性能進行測試。

實現

單例模式適用于資源占用較多的類,保證一個類只有一個實例即單例。通用的做法就是構造器私有化,提供一個全局的訪問點,返回類的實例。

uml圖:

java 單例的五種實現方式及其性能分析

1.餓漢式

代碼實現:

package com.zgh.gof23.singleton;
/**
 * 餓漢式
 * @author yuelin
 *
 */
public class SingleDemo {
 private static SingleDemo instance = new SingleDemo();
 //私有化構造器
 private SingleDemo() {
 //防止其他通過反射調用構造方法,破解單例
 if (instance != null) {
  throw new RuntimeException();
 }
 }

 //對外提供統(tǒng)一的訪問點
 public static SingleDemo getInstance() {
 return instance;
 }
}

優(yōu)點

1.實例的初始化由JVM裝載類的時候進行,保證了線程的安全性
2.實現簡單方便
3.實例的訪問效率高

缺點

1.不能實現懶加載,如果不調用getInstance(),那么這個類就白白的占據內存,資源的利用率不高
注意

1.防止通過反射調用構造方法破解單例模式。
2.防止通過反序列產生新的對象。

2.懶漢式

代碼實現:

package com.zgh.gof23.singleton;

/**
 * 懶漢式實現單例
 * 
 * @author zhuguohui
 *
 */
public class SingleDemo2 {
 // 此處并不初始化實例
 private static SingleDemo2 instance;

 private SingleDemo2() {
 if (instance != null) {
  throw new RuntimeException();
 }
 }

 /**
 * 當調用此方法的時候才初始化實例, 為了實現線程安全,需要使用同步方法
 * 
 * @return
 */
 public static synchronized SingleDemo2 getInstance() {
 if (instance == null) {
  instance = new SingleDemo2();
 }
 return instance;
 }
}

優(yōu)點

1.只有使用這個類的時候才初始化實例,優(yōu)化了資源利用率

缺點

1.為了實現線程安全,使用了同步方法獲取,增加了訪問的開銷

注意

1.防止通過反射調用構造方法破解單例模式。
2.防止通過反序列產生新的對象。

3.雙重檢查

代碼實現:

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.實現懶加載
2.通過縮小同步區(qū)域和第一次檢查提高訪問效率

缺點

1.為了實現線程安全,使用了同步方法獲取,增加了訪問的開銷

注意

1.防止通過反射調用構造方法破解單例模式。
2.防止通過反序列產生新的對象。

4.靜態(tài)內部類

代碼實現:

/**
 * 靜態(tài)內部類實現單例
 * 
 * @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();
 }
 }

 /**
 * 調用這個方法的時候,JVM才加載靜態(tài)內部類,才初始化靜態(tài)內部類的類變量。由于由JVM初始化,保證了線程安全性,
 * 同時又實現了懶加載
 * @return
 */
 public static SingleDemo4 getInstance() {
 return SingleDemo4Holder.instance;
 }
}

優(yōu)點

1.即實現了線程安全,又實現了懶加載

缺點

2.實現稍顯復雜

5.枚舉實現

代碼實現:

/**
 * 枚舉實現單例
 * 枚舉由JVM實現其的單例性
 * @author zhuguohui
 *
 */
public enum SingleDemo5 {
 INSTANCE;
}

優(yōu)點

1.實現簡單
2.線程安全
3.天熱對反射和反序列化漏洞免疫(由JVM提供)

缺點

2.不能實現懶加載

注意

1.防止通過反射調用構造方法破解單例模式。
2.防止通過反序列產生新的對象。

測試

源碼

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++) {
   //通過更換此處,來測試不同單例實現方式在多線程環(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 {
 //線程的總數量
 private int count;

 public CountLock(int count) {
 this.count = count;
 }

 /**
 * 當一個線程完成任務以后,調用一次這個方法
 */
 public synchronized void finish() {
 count--;
 if (count == 0) {
  notifyAll();
 }
 }

 /**
 * 需要等待其他線程執(zhí)行完的線程,調用此方法。
 */
 public synchronized void waitForWrok() {
 while (count > 0) {
  try {
  wait();
  } catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
  }
 }
 }
}

結果

五種單例實現方式,在100個線程下,每個線程訪問1千萬次實例的用時.

Tables 實現方式 用時(毫秒)
1 餓漢式 13
2 懶漢式 10778
3 雙重檢查 15
4 靜態(tài)內部類 14
5 枚舉 12

(*注意:由于不同電腦之間的性能差異,測試的結果可能不同)

總結

如果需要懶加載就使用靜態(tài)內部類方式,如果不需要就使用枚舉方式。

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持! 

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI