單例模式是設計模式中使用最廣泛的一種設計模式,它是對象建立模式,用於產生一個對象的具體實例,
確保系統中一個類只有一個實例。html
對於頻繁使用的對象,能夠省略建立對象花費的時間。同時因爲new操做的吃書減小,所以對系統內存
的使用頻率會下降,減輕了GC壓力,縮短了GC停頓時間。java
1.在類加載時初始化單例屬性(優勢:效率較高,多線程安全;缺點:增多程序加載時的額外負載)設計模式
public class ServiceConnation1 { /** * 類加載時就實例化單例屬性,此策略較爲安全,多線程時不會產生多個實例。可是當被實例化的 * 類的構造方法須要作大量工做、或作者遠程鏈接操做就會使得程序在啓動時耗去很長的時間,致 * 使啓動時間延長,用戶體驗下降。 */ private static ServiceConnation1 sc = new ServiceConnation1(); private InitialContext context; private ServiceConnation1() { try { /** * 此處並無指定properties屬性,只是示例簡要的結構,模擬遠程鏈接操做等 * 較爲耗時的初始化代碼,程序並不能運行成功。 */ context = new InitialContext(); } catch (NamingException e) { e.printStackTrace(); } } public static ServiceConnation1 getInstance() { return sc; } public Object getService(String name) throws NamingException { return context.lookup(name); } }
2.延遲加載策略(優勢:減小程序加載時的負載;缺點:須要引入同步,效率低下)安全
public class ServiceConnation2 { private static ServiceConnation2 sc = null; private InitialContext context; private ServiceConnation2() { try { /** * 此處並無指定properties屬性,只是示例簡要的結構,模擬遠程鏈接操做等 * 較爲耗時的初始化代碼,程序並不能運行成功。 */ context = new InitialContext(); } catch (NamingException e) { e.printStackTrace(); } } public static synchronized ServiceConnation2 getInstance() { /** * 在調用方法是判斷是否初始化單例對象,這樣能夠很好的解決上述啓動耗時的問題,方法在不添 * 加同步關鍵字的時候,在非多線程運行下較爲可靠,效率也較高。可是在多線程的狀況下可能會 * 產生多個實例,所以須要給方法添加synchronized修飾,這樣效率也大大下降了。 */ if(sc == null){ sc = new ServiceConnation2(); } return sc; } public Object getService(String name) throws NamingException { return context.lookup(name); } }
3.內部類延遲加載策略(改進前兩種的缺點,推薦使用)多線程
public class ServiceConnation3 { private InitialContext context; private ServiceConnation3() { try { /** * 此處並無指定properties屬性,只是示例簡要的結構,模擬遠程鏈接操做等 * 較爲耗時的初始化代碼,程序並不能運行成功。 */ context = new InitialContext(); } catch (NamingException e) { e.printStackTrace(); } } public static ServiceConnation3 getInstance() { /** * 爲了解決因延長加載而帶來的同步效率低下的問題,將程序改進爲內部類的方式實例化單例對象 */ return ServiceConnationHolder.sc; } public Object getService(String name) throws NamingException { return context.lookup(name); } /** * ServiceConnationHolder爲內部類,在程序初始化的收不會被加載,所以利用內部類的 * 這種特性能夠實現延遲加載,同時又不存在多線程安全帶的問題。 */ private static class ServiceConnationHolder { private static ServiceConnation3 sc = new ServiceConnation3(); } }
public class ServiceConnation { private ServiceConnation() { try { System.out.println("************do inital something************"); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } public static ServiceConnation getInstance() { return ServiceConnationHolder.instance; } public Object getService(String name) throws NamingException { System.out.println("************getService************"); return new Object(); } private static class ServiceConnationHolder{ public static ServiceConnation instance = new ServiceConnation(); } }
public class SingletonTest { @Test public void testInitalSingleton(){ ServiceConnation sc = ServiceConnation.getInstance(); try { sc.getService("beanName1"); sc = ServiceConnation.getInstance(); sc.getService("beanName2"); } catch (NamingException e) { e.printStackTrace(); } } }