最優雅的單例設計模式

1.單例設計模式是什麼

單例設計模式就是一個類,只生成一個對象 ,給予其他類調用。

所以至少至少要保證三點:

  1. 構造器私有化。

  2. 類本身創建對象。

  3. 提供獲取對象的方法

2.常見的幾種單例創建方式

2.1 餓漢式

在類加載到內存,就實例化一個實例。

jvm保證線程安全簡單實用,簡單以用,線程安全。

缺點:不管是否用到,類加載時完成實例化了。有人說這個會影響程序啓動時間,影響很小。

public class Singleton01 {
    // static 變量,在類加載時,初始化變量
    private static final Singleton01 instance = new Singleton01();

    //私有構造器,讓別人不能new
    private Singleton01(){}

    public static Singleton01 getInstance(){
        return instance;
    }

    public static void main(String[] args) {
        System.out.println(Singleton01.getInstance()==Singleton01.getInstance());
    }
}

2.2 懶漢式 lazy loading(雙重檢查)

實現按需加載,同時帶來了線程不安全的問題,。

解決線程安全(同時保證效率):雙重檢查(volatile synchronized),減小加鎖範圍synchronized,提高效率,同時線程安全。

缺點:代碼變複雜了。

public class Singleton06 {
    //雙重檢查volatile防止指令重排
    private static volatile Singleton06 instance;

    //私有構造器,讓別人不能new
    private Singleton06(){}
    //雙重檢查實現線程安全、效率較高的單例模式
    public static Singleton06 getInstance(){
        if(instance == null){
            synchronized(Singleton06.class){
                if(instance == null) {
                    instance = new Singleton06();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        System.out.println(Singleton06.getInstance()== Singleton06.getInstance());
    }
}

2.3 靜態內部類

jvm保證單例, 加載外部類時不會加載內部類,這樣就實現了懶加載,同時也保證了線程安全。

public class Singleton07 {

    //私有構造器,讓別人不能new
    private Singleton07(){}

    //靜態內部類,初始化 new Singleton07();
    //jvm只加載一次類,同時初始化靜態變量
    private static class Singleton07Holder{
        private final static Singleton07 instance = new Singleton07();
    }

    public static  Singleton07 getInstance(){
        return Singleton07Holder.instance;
    }
    public static void main(String[] args) {
        System.out.println(Singleton07.getInstance()== Singleton07.getInstance());
    }
}

2.4 枚舉實現

據說這種是最完美的一種。枚舉解決線程同步,以及反序列化問題。

public enum  Singleton08 {
    INSTANCE;

    public static void main(String[] args) {
        System.out.println(Singleton08.INSTANCE== Singleton08.INSTANCE);
    }
}

個人覺得:這四種都可以使用,推薦靜態內部類實現,但是沒有最好的,按需即可。

3.應用場景

只需要一個實例,秉承着多new一個實例,多浪費內存空間。

比如各種Manager、各種Factory。

我們開發中使用最多的是spring中ioc容器中的單實例bean的實例化。

在實例化對象之前,會鎖住singletonObjects map對象,具體如下。

synchronized (this.singletonObjects) {}

	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		//鎖定singletonObjects對象
		synchronized (this.singletonObjects) {
			//從緩存中獲取單例對象
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				if (this.singletonsCurrentlyInDestruction) {
					throw new BeanCreationNotAllowedException(beanName,
							"Singleton bean creation not allowed while singletons of this factory are in destruction " +
							"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
				}
				//單例創建前的檢查,並將其放入singletonsCurrentlyInCreation中
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
					/**
					 * 核心: 從{@link org.springframework.beans.factory.ObjectFactory#getObject()}加載bean
					 */
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						throw ex;
					} 
				}
				catch (BeanCreationException ex) {
					if (recordSuppressedExceptions) {
						for (Exception suppressedException : this.suppressedExceptions) {
							ex.addRelatedCause(suppressedException);
						}
					}
					throw ex;
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					//單例創建後的檢查,並將其從singletonsCurrentlyInCreation中移除
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

小小總結: 在開發過程中,只要不涉及自己重新搭建架構,基本上已經很少需要自己實例化單例了。spring已經幫我們完成了單實例的創建了。

面試必問設計模式,儘量從spring源碼中重新整理一遍23種設計模式。有興趣的同學可以看看我的源碼分析系列,目前正在分析spring源碼1、spring源碼解析之概況流程

未完待續。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章