對於單例模式咱們其實已經很是熟悉了,若是不熟悉的話,那你隨便百度一下就能看的一大堆關於單例模式的介紹。單例模式的實現是當前設計模式中能夠說實現最簡單的一種模式了,也很容易理解,可是其實單例模式並不是你想的那麼簡單哦。
下面就從幾個實際的源碼中來看看,單例模式在什麼樣的狀況下會被使用。java
問你下面幾個問題,若是你對這幾個問題爛熟於心,那麼證實你真的瞭解單例模式。spring
一、有幾種常見的實現單例模式的方法?
二、經過序列化和反序列化能夠破壞某些單例模式
三、經過反射能夠破壞某些單例模式
四、能夠用枚舉實現單例模式,而且很是安全
五、如何針對每個線程單首創建一個單例設計模式
單例模式的實現有不少方式,這裏只是我的作一個記錄,供參考。緩存
package com.linkinstars.singleton; /** * (雙重檢查)單例模式 * @author LinkinStar */ public class DoubleCheckSingleton { private DoubleCheckSingleton(){} public volatile static DoubleCheckSingleton doubleCheckSingleton = null; public static DoubleCheckSingleton getInstance(){ if (doubleCheckSingleton == null) { synchronized(DoubleCheckSingleton.class){ if (doubleCheckSingleton == null) { doubleCheckSingleton = new DoubleCheckSingleton(); } } } return doubleCheckSingleton; } }
package com.linkinstars.singleton; /** * 餓漢式單例 * @author LinkinStar */ public class HungrySingleton { private final static HungrySingleton INSTANCE = new HungrySingleton(); public static HungrySingleton getInstance() { return INSTANCE; } private HungrySingleton() { if (INSTANCE != null) { throw new RuntimeException("Can't create singleton by reflect."); } } }
package com.linkinstars.singleton; /** * 靜態內部類實現單例模式 * @author LinkinStar */ public class StaticInnerClassSingleton { private static class InnerClass { private static StaticInnerClassSingleton staticInner = new StaticInnerClassSingleton(); } public StaticInnerClassSingleton getInstance(){ return InnerClass.staticInner; } private StaticInnerClassSingleton(){} }
package com.linkinstars.singleton; /** * 枚舉實現單例模式 * @author LinkinStar */ public enum EnumSingleton { /** 單例 **/ INSTANCE; private Object data; public Object getData() { return data; } public void setData(Object data) { this.data = data; } public static EnumSingleton getInstance() { return INSTANCE; } }
package com.linkinstars.singleton; /** * 單線程級別的單例 * @author LinkinStar */ public class ThreadLocalSingleton { public static final ThreadLocal<ThreadLocalSingleton> THREAD_LOCAL_SINGLETON_THREAD_LOCAL = ThreadLocal.withInitial(ThreadLocalSingleton::new); private ThreadLocalSingleton(){ } public static ThreadLocalSingleton getInstance(){ return THREAD_LOCAL_SINGLETON_THREAD_LOCAL.get(); } }
jdk中有一個Runtime的類,這個類用於獲取java程序運行時的一些環境,如內存等信息。這些獲取信息的方法不少都是會調用底層原生的native方法。
如:Runtime.getRuntime().maxMemory();
而後咱們進入Runtime能夠看見源碼中:
安全
這個是標準的餓漢式的單例
由於java程序運行時,若是要獲取運行時的環境信息,總須要一個載體,而且在任何地方獲取的都應該是同樣的,因此用一個單例來維護這個信息,而且提供些方法,再好不過了。多線程
Spring單例模式的bean只會建立一次,若是再獲取該bean,是直接從單例緩存中獲取,該過程就在getSingleton(String beanName)中。
優化
這裏的方法須要你細細品讀,我要說明的是,單例模式確定有本身的好處,否則spring爲何bean默認是單例的呢?很簡單的一點是由於單例的速度快,而且對象少,佔用的內存就少,若是是多例的,每次使用須要進行建立會耗時間,而且消耗內存。
可是同時單例模式也有相應的問題,就是多線程訪問的時候會有線程安全的問題,多個線程操做私有變量的時候就會出現。因此在使用的單例模式的時候也要同時考慮到線程安全的問題。this
ErrorContext是MyBatis中的一個頗有意思的類,從名字咱們也能夠猜到,它是用於記錄當前線程的發生錯的信息,獲取到當前錯誤發生的上下文信息。
spa

咱們能夠想到,當咱們每一個線程出現錯誤的時候不可能去影響別的線程的錯誤信息的記錄,每一個線程確定記錄屬於本身的錯誤信息,因此ErrorContext一開始被建立,當獲取的時候,針對於每一個線程自己都是獲取到的對象應該是同樣的。線程
單例模式使用的不少,理解它的目的和優勢知道它的使用場景,能夠對你的系統設計上面有更多優化的可能性。