1.介紹
單例模式是一種對象建立型模式,保證一個類隻有一個執行個體,其私有化構造方法公開一個全局通路的代理方法。
2.應用場景
- 作業系統的任務管理器
- 讀取配置檔案的類
- 資料庫連接配接池
- Javaweb 中的 Servlet 執行個體
- Spring 建立的執行個體,預設為單例
3.實作方式
/**
* 餓漢式單例
*/
public class EagerSingleton {
private static EagerSingleton eagerSingleton = new EagerSingleton();
private EagerSingleton() {
}
public static EagerSingleton getInstance() {
return eagerSingleton;
}
}
優點:線程安全,不用加同步鎖,是以在高并發時調用效率高。
缺點:不能懶加載,如果不使用該類的執行個體,浪費記憶體資源。
/**
* 懶漢式
*/
public class LazySingleton {
private static LazySingleton lazySingleton;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
優點:實作懶加載,合理利用系統資源。
缺點:需要添加同步鎖,高并發時調用效率不高。
(注意:懶漢式可以通過反射機制建立多個執行個體。)
public class SingletonTest {
public static void main(String[] args)throws Exception {
Class<?> clazz = Class.forName("com.design.singleton.LazySingleton");
Constructor<?> constructor = clazz.getDeclaredConstructor(null);
// 跳過檢測機制
constructor.setAccessible(true);
LazySingleton lazySingleton1 = (LazySingleton) constructor.newInstance();
LazySingleton lazySingleton2 = (LazySingleton) constructor.newInstance();
System.out.println(lazySingleton1 == lazySingleton2);// 結果傳回 false
}
}
優化方式:
public class Optimizing {
private Optimizing() {
}
private static class innerSingleton{
private static final Optimizing optimizing1 = new Optimizing();
}
public static Optimizing getInstance() {
return innerSingleton.optimizing1;
}
}
外部類沒有靜态屬性,是以不會像餓漢式立即加載對象。
隻有當調用公共方法(getInstance)時,才會加載靜态内部類。加載内部類的過程是線程安全的。
内部類中通過 static final 確定記憶體中隻有一個外部類的執行個體,因為執行個體變量(optimizing1)隻能被指派一次。
測試:
測試結果是相對的,硬體等環境的不同會有影響,大緻上的性能比例差不多。
public static void main(String[] args) throws Exception {
// 線程數
int num = 10;
// 計數器
CountDownLatch cd = new CountDownLatch(num);
long t1 = System.currentTimeMillis();
for (int i = 0; i < num; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100000; i++) {
// 此處替換不同實作方式的單例代碼進行測試
// EagerSingleton eagerSingleton = EagerSingleton.getInstance();//耗時:17ms
// LazySingleton lazySingleton =LazySingleton.getInstance();//耗時:19ms
// Optimizing optimizing = Optimizing.getInstance();//耗時:17ms
}
cd.countDown();
}
}).start();
}
// 主線程等待
cd.await();
System.out.println("耗時:" + (System.currentTimeMillis() - t1) + "ms");
}