您現在的位置是:首頁 > 籃球

喪心病狂!面試官讓我介紹:單例模式的使用場景及“12種”寫法

  • 由 還有頭髮還能學 發表于 籃球
  • 2021-12-03
簡介private Singletion() {}public static Singleton getInstance() {return instance

為什麼要使用單例模式

1。 什麼是單例模式

單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種型別的設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。

這種模式涉及到一個單一的類,該類負責建立自己的物件,同時確保只有單個物件被建立。這個類提供了一種訪問其唯一的物件的方式,可以直接訪問,不需要例項化該類的物件。

保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。

喪心病狂!面試官讓我介紹:單例模式的使用場景及“12種”寫法

2。 單例模式的優點/缺點和使用場景

2.1 單例模式的優點

提供了對唯一例項的受控訪問

由於系統中記憶體只存在一個物件,因此可以節約系統的的資源,對於一些頻繁的建立和銷燬的物件單例模式無疑可以提高系統的效能

單例模式可以允許可變的數目的例項,使用單例模式進行擴充套件,使用控制單利物件相似的方法獲取指定個數的例項,及解決了單利物件,共享過多,而有損效能的問題

2.2 單例模式的缺點

由於單例模式,不是抽象的所以可擴充套件性比較差

職責過重,在一定程度上違背了單一職責

濫用單例將帶來一些負面的問題,如為了節省資源將資料庫連線池物件設計為單例模式,可能會導致共享連線池物件的程式過多未出而出現的連線池溢位,如果例項化物件長時間不用系統就會被認為垃圾物件被回收,這將導致物件狀態丟失

2.3 單例模式的使用場景

開發工具類庫中的很多工具類都應用了單例模式,比例執行緒池、快取、日誌物件等,它們都只需要建立一個物件。

3。 單例模式的12種寫法

3.1 餓漢式(靜態變數)

public class Singleton { private static Singleton instance = new Singletion(); private Singletion() { } public static Singleton getInstance() { return instance; }}

3.2 餓漢式(靜態常量)

public class Singleton { private final static Singleton INSTANCE = new Singleton(); private Singleton() { } public static Singleton getInstance() { return INSTANCE; }}

3.3 餓漢式(靜態程式碼塊)

public class Singleton { private static Singleton instance; static { instance = new Singleton(); } private Singleton() { } public static Singleton getInstance() { return instance; }}

上面三種寫法本質上其實是一樣的,也是各類文章在介紹餓漢式時常用的方式。但使用靜態final的例項物件或者使用靜態程式碼塊依舊不能解決在反序列化、反射、克隆時重新生成例項物件的問題。

序列化:一是可以將一個單例的例項物件寫到磁碟,實現資料的持久化;二是實現物件資料的遠端傳輸。

當單例物件有必要實現 Serializable 介面時,即使將其建構函式設為私有,在它反序列化時依然會透過特殊的途徑再建立類的一個新的例項,相當於呼叫了該類的建構函式有效地獲得了一個新例項!

3.4 懶漢式(執行緒不安全)

public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }}

3.5 懶漢式(執行緒安全,存在同步開銷)

class Singleton { private static Singleton intance = null; private Singleton() { //私有建構函式 } public static synchronized Singleton getInstance() { if (intance == null) { intance = new Singleton(); } return intance; }}

3.6 懶漢式(執行緒假裝安全,同步程式碼塊)

class Singleton { private static Singleton singleton; private Singleton() { } public static Singleton getInstance() { if (singleton == null) { // 4。加入同步程式碼塊 synchronized (Singleton。class) { singleton = new Singleton(); } } return singleton; }}

3.7 DCL「雙重檢測鎖:Double Checked Lock」(假)

public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton。class) { if (instance == null) { instance = new Singleton(); } } } return instance; }}

3.8 DCL「雙重檢測鎖:Double Checked Lock」 單例(真)

public class Singleton { private static volatile Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton。class) { instance = new Singleton(); } } return instance; }}

3.9 靜態內部類(推薦使用)

public class Singleton { private Singleton() { } private static class SingletonInstance() { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonInstance。INSTANCE; }}

3.10 列舉類單例模式

public enum Singleton { INSTANCE; private Resource instance; Singleton() { instance = new Resource(); } public Resource getInstance() { return instance; } public static class Resource { private Resource() { } }}

3.11 登記式單例--使用Map容器來管理單例模式

public class Singleton { private static Map map = new HashMap<>(); public static void reglisterService(String key, Object instance) { if (!map。containsKey) { map。put(key, instance); } } public static Object getInstance(String key) { return map。get(key); }}

3.12 內部列舉類

public interface MySingleton { void doSomething();}public enum Singleton implements MySingleton { INSTANCE { @Override public void doSomething() { System。out。println(“complete singleton”); } }; public static MySingleton getInstance() { return Singleton。INSTANCE; }}

4。Spring依賴注入對單例的使用

在AbstractBeanFactory中

@Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } @Override public T getBean(String name, Class requiredType) throws BeansException { return doGetBean(name, requiredType, null, false); } @Override public Object getBean(String name, Object。。。 args) throws BeansException { return doGetBean(name, null, args, false); }

4.1 doGetBean中getSingleton

protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { String beanName = transformedBeanName(name); Object bean; // Eagerly check singleton cache for manually registered singletons。 Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { if (logger。isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger。trace(“Returning eagerly cached instance of singleton bean ‘” + beanName + “’ that is not fully initialized yet - a consequence of a circular reference”); } else { logger。trace(“Returning cached instance of singleton bean ‘” + beanName + “’”); } } bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // Fail if we‘re already creating this bean instance: // We’re assumably within a circular reference。 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory。 BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent。 String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory)。doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegation to parent with explicit args。 return (T) parentBeanFactory。getBean(nameToLookup, args); } else if (requiredType != null) { // No args -> delegate to standard getBean method。 return parentBeanFactory。getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory。getBean(nameToLookup); } } if (!typeCheckOnly) { markBeanAsCreated(beanName); } StartupStep beanCreation = this。applicationStartup。start(“spring。beans。instantiate”) 。tag(“beanName”, name); try { if (requiredType != null) { beanCreation。tag(“beanType”, requiredType::toString); } RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on。 String[] dependsOn = mbd。getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd。getResourceDescription(), beanName, “Circular depends-on relationship between ‘” + beanName + “’ and ‘” + dep + “’”); } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd。getResourceDescription(), beanName, “‘” + beanName + “’ depends on missing bean ‘” + dep + “’”, ex); } } } // Create bean instance。 if (mbd。isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution。 // Also remove any beans that received a temporary reference to the bean。 destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd。isPrototype()) { // It‘s a prototype -> create a new instance。 Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { String scopeName = mbd。getScope(); if (!StringUtils。hasLength(scopeName)) { throw new IllegalStateException(“No scope name defined for bean ” + beanName + “’”); } Scope scope = this。scopes。get(scopeName); if (scope == null) { throw new IllegalStateException(“No Scope registered for scope name ‘” + scopeName + “’”); } try { Object scopedInstance = scope。get(beanName, () -> { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new ScopeNotActiveException(beanName, scopeName, ex); } } } catch (BeansException ex) { beanCreation。tag(“exception”, ex。getClass()。toString()); beanCreation。tag(“message”, String。valueOf(ex。getMessage())); cleanupAfterBeanCreationFailure(beanName); throw ex; } finally { beanCreation。end(); } } // Check if required type matches the type of the actual bean instance。 if (requiredType != null && !requiredType。isInstance(bean)) { try { T convertedBean = getTypeConverter()。convertIfNecessary(bean, requiredType); if (convertedBean == null) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean。getClass()); } return convertedBean; } catch (TypeMismatchException ex) { if (logger。isTraceEnabled()) { logger。trace(“Failed to convert bean ‘” + name + “’ to required type ‘” + ClassUtils。getQualifiedName(requiredType) + “’”, ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean。getClass()); } } return (T) bean; }

4.2 getSingleton的實現

返回在給定名稱下注冊的(原始)單例物件,檢查已經例項化的單例並允許提前 對當前建立的單例的引用(解析迴圈引用)。

public Object getSingleton(String beanName) { return getSingleton(beanName, true); }@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { // Quick check for existing instance without full singleton lock Object singletonObject = this。singletonObjects。get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { singletonObject = this。earlySingletonObjects。get(beanName); if (singletonObject == null && allowEarlyReference) { synchronized (this。singletonObjects) { // Consistent creation of early reference within full singleton lock singletonObject = this。singletonObjects。get(beanName); if (singletonObject == null) { singletonObject = this。earlySingletonObjects。get(beanName); if (singletonObject == null) { ObjectFactory<?> singletonFactory = this。singletonFactories。get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory。getObject(); this。earlySingletonObjects。put(beanName, singletonObject); this。singletonFactories。remove(beanName); } } } } } } return singletonObject; }

作者:ZhangSan_Plus

連結:https://juejin。cn/post/7006124074338369567

Top