定義
單例設計模式(Singleton),就是採取一定的方法保證在整個的軟件系統中,爲了節約硬件資源或保證數據一致性,某個類只能存在一個對象實例,並且該類只提供一個取得其對象實例的方法(靜態方法)。
單例模式屬於創建型模式。
要點
- 該類只能有一個實例
- 單例類自行創建實例
構造器私有化 - 向整個系統提供獲取這個實例的統一訪問方式
對外提供獲取該實例對象的方式 (1)直接暴露;(2)公有靜態方法
場景
某個類在整個系統中只能有一個實例對象可被獲取和使用的代碼模式。
需要頻繁的進行創建和銷燬的對象、創建對象時耗時過多或耗費資源過多(即:重量級對象),但又經常用到的對象、工具類對象、頻繁訪問數據庫或文件的對象(比如數據源、session工廠等)
例如:代表 JVM 運行環境的 Runtime 類( 餓漢式 )。
實現
- 餓漢式 - 直接初始化
/**
* 第一種形式:餓漢式 - 直接初始化
*
* 在類裝載時完成實例化。利用 classloader 機制避免了線程同步問題。
* 沒有達到 Lazy Loading 的效果,如果從始至終從未使用過這個實例,則會造成內存的浪費。
*/
public class Singleton01 {
public static final Singleton01 INSTANCE = new Singleton01();
private Singleton01() {}
}
2.餓漢式 - 枚舉
/**
* 第二種形式:餓漢式 - 枚舉
*
* JDK 1.5+
* 避免多線程問題,還能防止反序列化重新創建新的對象,Effective Java 作者 Josh Bloch 提倡的方式
*/
public enum Singleton02 {
/**
* 枚舉單例
*/
INSTANCE
}
- 餓漢式 - 靜態代碼塊
/**
* 第三種形式:餓漢式 - 靜態代碼塊
*
* 適合複雜實例化
* 線程安全
*/
public class Singleton03 {
public static final Singleton03 INSTANCE;
private String initialInfo;
static {
try {
Properties properties = new Properties();
properties.load(Singleton03.class.getClassLoader().getResourceAsStream("singleton.peroperties"));
INSTANCE = new Singleton03(properties.getProperty("SingletonInfo"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private Singleton03(String initialInfo) { this.initialInfo = initialInfo; }
}
- 懶漢式 - 單線程使用
/**
* 第四種形式:懶漢式 - 單線程
*
* 線程不安全
*/
public class Singleton04 {
private static Singleton04 instance;
private Singleton04() {}
public static Singleton04 getInstance() {
if (instance == null) {
instance = new Singleton04();
}
return instance;
}
}
5.== 懶漢式 - 雙重判斷==
/**
* 第五種形式:懶漢式 - 雙重判斷
*
* 適用於多線程
* 注意:這裏的 instance 類變量需要使用volatile。
*/
public class Singleton05 {
private static volatile Singleton05 instance;
private Singleton05() {}
public static Singleton05 getInstance() {
// 此處判斷是爲了提高性能
if (instance == null) {
synchronized (Singleton05.class) {
if (instance == null) {
instance = new Singleton05();
}
}
}
return instance;
}
}
- 懶漢式 - 內部類
/**
* 第六種形式:懶漢式 - 內部類
*
* 在內部類初始化時,才創建Singleton實例
* 靜態內部類不會隨着外部類的加載和初始化而初始化.
* 靜態內部類和非靜態內部類一樣,都是在被調用時纔會被加載,它是單獨去加載和初始化的,在內部類加載和初始化時創建對象,所以是線程安全的。
*
* 實現懶加載且線程安全,推薦
*/
public class Singleton06 {
private Singleton06() {}
private static class Inner {
private static final Singleton06 INSTANCE = new Singleton06();
}
public static Singleton06 getInstance() {
return Inner.INSTANCE;
}
}
源代碼
Click here:Github 地址