1.餓漢式
//final不允許類被繼承
public final class Singleton {
//實例化變量
private byte[] data=new byte[1024];
//直接初始化
private static Singleton instance=new Singleton();
//私有構造函數,不允許外部new
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
}
如果主動使用Singleton類,instance實例將直接完成創建,包括其中的實例變量都會得到初始化,但是instance可能被加載很長一段時間后才被使用,instance實例開辟的堆內存會駐留更久的時間,如果說一個類的成員變量不多,且占用內存資源較少,可以使用餓漢式,總結它可以保證多個線程下唯一實例,getInstance方法性能較高,但是無法進行懶加載。
2.懶漢式
//final不允許類被繼承
public final class Singleton {
//實例化變量
private byte[] data=new byte[1024];
private static Singleton instance=null;
//私有構造函數,不允許外部new
private Singleton(){
}
//等到需要使用時進行創建
public static Singleton getInstance(){
if (instance==null){
instance=new Singleton();
}
return instance;
}
}
該方法在多線程環境下不能保證單例的唯一性。
3.懶漢+同步方法
//final不允許類被繼承
public final class Singleton {
//實例化變量
private byte[] data=new byte[1024];
private static Singleton instance=null;
//私有構造函數,不允許外部new
private Singleton(){
}
//等到需要使用時進行創建
public static synchronized Singleton getInstance(){
if (instance==null){
instance=new Singleton();
}
return instance;
}
}
可以保證在多線程環境下單例的唯一性,但是synchronied關鍵字會導致在同一時刻方法只能被一個線程所訪問,性能低下。
4.雙重鎖檢查
//final不允許類被繼承
public final class Singleton {
//實例化變量
private byte[] data=new byte[1024];
private static Singleton instance=null;
//私有構造函數,不允許外部new
private Singleton(){
}
//等到需要使用時進行創建
public static Singleton getInstance(){
if (instance==null){
synchronized (Singleton.class){
if (instance==null) instance=new Singleton();
}
}
return instance;
}
}
這段代碼看起來很完美,很可惜,它是有問題。主要在於instance = new Singleton()這句,這並非是一個原子操作,事實上在 JVM 中這句話大概做了下面 3 件事情:
1.給 instance 分配內存
2.調用 Singleton 的構造函數來初始化成員變量
3.將instance對象指向分配的內存空間(執行完這步 instance 就為非 null 了)
但是在 JVM 的即時編譯器中存在指令重排序的優化。也就是說上面的第二步和第三步的順序是不能保證的,最終的執行順序可能是 1-2-3 也可能是 1-3-2。如果是后者,則在 3 執行完畢、2 未執行之前,被線程二搶占了,這時 instance 已經是非 null 了(但卻沒有初始化),所以線程二會直接返回 instance,然后使用,然后順理成章地報錯。
5.volatile+雙重鎖檢查
//final不允許類被繼承
public final class Singleton {
//實例化變量
private byte[] data=new byte[1024];
//禁止指令重排序
private volatile static Singleton instance=null;
//私有構造函數,不允許外部new
private Singleton(){
}
//等到需要使用時進行創建
public static Singleton getInstance(){
if (instance==null){
synchronized (Singleton.class){
if (instance==null) instance=new Singleton();
}
}
return instance;
}
}
加上volatile禁止了指令的重排許操作。滿足多線程下的單例,懶加載,獲取實例的高效性。
6.Holder方式
//final不允許類被繼承
public final class Singleton {
//實例化變量
private byte[] data=new byte[1024];
//私有構造函數,不允許外部new
private Singleton(){
}
//在靜態內部類中持有singleton的實例,可以被直接初始化
private static class Holder{
private static Singleton instance=new Singleton();
}
public static Singleton getInstance(){
return Holder.instance;
}
}
Holder類中定義了Singleton的靜態變量,並且直接進行了實例化,當Holder被主動引用的時候會創建Singleton實例。Holder方式的單例模式是最好的設計之一,也是目前用的比較廣泛的設計之一。
7.枚舉方式
枚舉類型不允許被繼承,同樣是線程安全的且只能被實例化一次,但是枚舉類型不能夠懶加載,對Singleton主動使用,比如調用其中的靜態方法則INSTANCE會立即得到實例化。
public enum Singleton{
INSTANCE;
private byte[] data=new byte[1024];
Singleton(){
System.out.println("INSTANCE will be initialized immediately");
}
public static void method(){
//調用該方法會主動使用Singleton,INSTANCE實例將會被實例化
}
public static Singleton getInstance(){
return INSTANCE;
}
}
