本文按部就班介紹單例模式的幾種實現方式,以及Jdk中使用到單例模式的例子,以及sring框架中使用到的單例模式例子。java
package signgleton; /** * 單例模式簡單的實現 */ public class Singleton { private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } }
」餓漢式「只是形象的比喻,由於他想要這個實例的時候,不須要等待,別廢話,給哥拿來。經過static的初始化方式,藉助類第一次被加載時,就把Singleton實例給建立出來了,並存儲在JVM的方法區,屬於類變量,被全部的實例共享。不一樣的線程調用都返回一個實例,因此這樣也保證了線程安全。spring
它還有個孿生兄弟,靜態代碼塊來實例化:編程
package signgleton; /** * 經過靜態代碼塊建立實例對象 */ public class StaticSignleton { private static StaticSignleton instance; /** * 靜態代碼塊建立實例 */ static { instance = new StaticSignleton(); } private StaticSignleton() { } public static StaticSignleton getInstance() { return instance; } }
科普一下類初始化順序:緩存
餓漢式缺點:由於在類被加載的時候對象就會被實例化,這可能會形成沒必要要的消耗,若是你的程序不在意這點消耗那就當我沒說。安全
下面介紹兩種方式解決上面的問題:第一是使用靜態內部類,第二是使用懶漢式多線程
package signgleton; /** * 使用靜態內部類獲取單例實例 */ public class StaticInnerClassSingleton { private static class InnerSingletonClass{ private static final StaticInnerClassSingleton innerInstance = new StaticInnerClassSingleton(); } private StaticInnerClassSingleton() { } public static final StaticInnerClassSingleton getStaticInstance() { return InnerSingletonClass.innerInstance; } }
靜態內部類一樣藉助了JVM這個大佬來保證線程安全,只是他在類加載的時候並無當即實例化對象,而是採用了延遲加載策略,只有調用getStaticInstance的時候才用內部類去建立實例框架
package signgleton; /** * 線程不安全的懶漢式 */ public class UnsafeSingleton { private static UnsafeSingleton unsafeSingleton; private UnsafeSingleton() { } public static UnsafeSingleton getUnsafeSingleton() { if (unsafeSingleton == null) { unsafeSingleton = new UnsafeSingleton(); } return unsafeSingleton; } }
雖然這樣寫達到了使用的時候才實例化的目的,可是也帶來的線程安全問題。在多線程下,可能有兩個以上的線程同時進入if(unsafeInstance == null),這樣會發生一些奇怪不定的結果。ide
package signgleton; /** * 線程安全的懶漢式 */ public class SafeSingleton { private static SafeSingleton safeSingleton; private SafeSingleton() { } public static synchronized SafeSingleton getSafeSingleton() { if (safeSingleton == null) { safeSingleton = new SafeSingleton(); } return safeSingleton; } }
這種方式在方法上加synchronized同步關鍵字解決了餓漢式線程安全問題,可是由於每次調用都加鎖,極大地下降了性能,由於只有第一次建立實例時須要加鎖,弄成如今每次都加鎖。有沒有解決辦法呢,固然有,前輩們都是很聰明的,想出了雙重校驗鎖這個經典的例子.函數
package signgleton; /** * 線程不安全雙重校驗鎖 */ public class UnSafeTwoCheckSingleton { private static UnSafeTwoCheckSingleton singleton; private UnSafeTwoCheckSingleton() { } public static UnSafeTwoCheckSingleton getSingleton() { if (singleton == null) { synchronized (UnSafeTwoCheckSingleton.class) { if (singleton == null) { singleton = new UnSafeTwoCheckSingleton(); } } } return singleton; } }
雙重校驗鎖的形式主要是縮小了鎖的範圍,可是熟悉多線程編程的同窗就能夠看得出來,即便這樣作仍是有線程安全問題,這裏存在一個多個線程共享變量的可見性問題(這部分我不太懂原理),解決方案就是使用volatile性能
使用volatile優化
package signgleton; /** * 線程安全雙重校驗鎖 */ public class SafeTwoCheckSingleton { private static volatile SafeTwoCheckSingleton singleton; private SafeTwoCheckSingleton() { } public static SafeTwoCheckSingleton getSingleton() { if (singleton == null) { synchronized (SafeTwoCheckSingleton.class) { if (singleton == null) { singleton = new SafeTwoCheckSingleton(); } } } return singleton; } }
你覺得這樣就安全了嗎,就想下班了嗎?還沒完,序列化這個惡棍會破壞單例,防範序列化這個惡棍破壞單例,能夠在類中定義咱們獲取實例的策略,既加readResolve。
package signgleton; import java.io.Serializable; /** * 線程安全雙重校驗鎖 */ public class SafeTwoCheckSingleton implements Serializable{ private static volatile SafeTwoCheckSingleton singleton; private SafeTwoCheckSingleton() { } public static SafeTwoCheckSingleton getSingleton() { if (singleton == null) { synchronized (SafeTwoCheckSingleton.class) { if (singleton == null) { singleton = new SafeTwoCheckSingleton(); } } } return singleton; } private Object readResolve() { return singleton; } }
這麼多的實現方式,你會問,有什麼用?用處可大了,下面講兩個使用實例,一個jdk的Runtime, 一個是Spring框架中的單例模式。
Runtime:是一個封裝了JVM進程的類,每個JAVA程序實際上都是JVM的一個進程,每個進程都是對應這麼一個Runtime實例。
源碼以下:
public class Runtime { private static Runtime currentRuntime = new Runtime(); public static Runtime getRuntime() { return currentRuntime; } private Runtime() {} }
這裏使用了餓漢式單例模式。
下面咱們來看看看spring 中的單例模式,spring中使用的是單例註冊表的特殊方式實現的單例模式,因此說模式是死的,須要靈活得運用。
看看單例註冊表的實現原理demo:
package signgleton; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 單例註冊表demo */ public class SingletonRegTest { private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<String,Object>(); /** * 類加載時初始化一個實例 */ static { SingletonRegTest singletonRegTest = new SingletonRegTest(); singletonObjects.put(singletonRegTest.getClass().getName(), singletonRegTest); } public static SingletonRegTest getInstance(String name) { if (name == null) { // 默認分配一個實例 name = "signgleton.SingletonRegTest"; } if (singletonObjects.get(name) == null) { try { // 將默認實例放入緩存中 singletonObjects.put(name, Class.forName(name).newInstance()); } catch (Exception ex) { ex.printStackTrace(); } } return (SingletonRegTest) singletonObjects.get(name); } }
再來看看spring 源碼:
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { @SuppressWarnings("unchecked") protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; // 從單例註冊表中檢查是否存在單例緩存 Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { ... // 返回緩存實例 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { ... try { ... // 若是是單例模式 if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { ... } } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // 若是是原型模式 else if (mbd.isPrototype()) { ... } // 其餘模式 else { ... } } catch (BeansException ex) { ... } } return (T) bean; } }
咱們進入 getSingleton()方法:
import java.util.Map; public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { // 經過 ConcurrentHashMap 實現單例註冊表 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64); public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "'beanName' must not be null"); synchronized (this.singletonObjects) { // 檢查緩存中是否存在實例 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { ... try { singletonObject = singletonFactory.getObject(); } catch (BeanCreationException ex) { ... } finally { ... } // 若是實例對象在不存在,咱們註冊到單例註冊表中。 addSingleton(beanName, singletonObject); } return (singletonObject != NULL_OBJECT ? singletonObject : null); } } protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT)); } } }
是否是和咱們的單例註冊表demo很類似。單例模式的講解後面隨着學習到其餘框架再作相應的補充,也歡迎你們獻言獻策。