設計模式-單例

單例模式

定義

確保某一個類只有一個實例,並且自行實例化並向整個系統提供這個實例

優勢

1. 內存中只有一個實例,減小內存開支
2. 減小對象頻繁的建立、銷燬
3. 建立資源時須要比較多的資源時減小,減小性能開銷
4. 避免對資源的多重佔用
5. 在系統設置全局訪問點,優化和共享資源訪問

缺點

1. 沒有接口,擴展困難
2. 與單一原則職責有衝突,一個類應該只實現本身的邏輯,並不須要關心它是否單例的,
   是否是單例取決與環境,單例模式把 「要單例」和業務邏輯融合了(能夠用內部類持有單例的方式解決此問題)

使用場景

系統要求某個類有且只有一個實例,不然就會出現「不良反應」
  • 具體場景舉例java

    生成惟一的序列號
      項目中須要一個共享訪問點或共享數據

擴展

須要產生多個固定數量或有上限數量對象的模式就叫多例模式。能夠在設計系統的時候決定系統產生多少個
實例,方便進行擴展,修正單例存在的性能問題,提供響應速度

實現

· 簡單類的單例安全

餓漢式
類被加載時就會被實例化一個對象

· 優勢ide

線程安全

· 缺點性能

不能被延遲加載
沒有被調用狀況,浪費系統資源

· 實現方式測試

  1. 簡單的類的單例
public class SimpleHungrySingleInstance {

	private static final SimpleHungrySingleInstance instance = new SimpleHungrySingleInstance();

	private SimpleHungrySingleInstance(){

	}

	public static SimpleHungrySingleInstance getSingleInstance(){
		return instance;
	}

}
  1. 枚舉式

枚舉只能擁有私有的構造器優化

枚舉類其實是一個繼承 Enum 的一個 final 類線程

枚舉類不容許被反序列化,Enum 重寫了方法設計

靜態代碼塊中對 final 變量的值進行初始化指針

enum 類最終是一個 final classcode

/**
 * 利用枚舉實現單例
 *
 */
public class ThreadSafeSingleInstanceFactoryFive {

	private ThreadSafeSingleInstanceFactoryFive(){

	}

	private  enum SingleFactoryEnum{

		SINGLE_FACTORY_ENUM {
			@Override
			public void doSomeThing() {
				 
			}
		};

		private  Cat cat = null;
		public  void doSomeThing(){
			System.out.println("cat.hashCode=" + cat.hashCode());
		}

		SingleFactoryEnum(){
			cat = new Cat();
		}
	}

	public static Cat getCat(){
		return SingleFactoryEnum.SINGLE_FACTORY_ENUM.cat;
	}

    public static void doSomeThing(){
		 SingleFactoryEnum.SINGLE_FACTORY_ENUM.doSomeThing();
	}
懶漢式
只在須要對象時纔會生成單例對象

· 優勢

能延遲加載

· 缺點

須要本身保證線程安全

· 實現方式

  1. synchronized 方法獲取

    線程安全,鎖粒度爲Class,粒度較大,性能受影響

    public class ThreadSafeSingleInstanceModelOne {
    
    	private static ThreadSafeSingleInstanceModelOne threadSafeSingleInstanceModelOne = null;
    
    	private ThreadSafeSingleInstanceModelOne(){
    
    	}
    
    	/**
    	 *
    	 * @return
    	 */
    	public synchronized static ThreadSafeSingleInstanceModelOne getInstance(){
    
    		if(threadSafeSingleInstanceModelOne == null){
    			try {
    				Thread.sleep(300); //模擬實例化須要的時間
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			threadSafeSingleInstanceModelOne = new ThreadSafeSingleInstanceModelOne();
    		}
    			return threadSafeSingleInstanceModelOne;
    		}
    	}
  2. synchronized 同步塊

    非線程安全

public class ThreadSafeSingleInstanceModelTwo {

	private static ThreadSafeSingleInstanceModelTwo instance = null;

	public static ThreadSafeSingleInstanceModelTwo getInstance(){
		if(instance == null){   // 多個線程可能會同時在這裏判true,所以屢次進入同步塊
			synchronized (ThreadSafeSingleInstanceModelTwo.class){

				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				instance = new ThreadSafeSingleInstanceModelTwo();
			}
		}

		return instance;
		}
	}
  1. 採用volatileh和DCL(雙檢查鎖機制)
public class ThreadSafeSingleInstanceModelThree {

	/**
	 *   採用Volatile 和 DCL機制
	 *
	 * volatile 關鍵字做用 只能保證可見性
	 * 1,多個線程可見
	 * 2,禁止指令重排
	 *
	 * 通常對象的建立步驟   分配地址 -> 初始化屬性 -> 指針指向分配地址
	 *
	 * 編譯器優化致使指令重排 可能出現
	 * 			分配地址
	 * 			-> 指針指向分配地址(沒有初始化 返回是個null對象  原始類型範圍內的值(例如int 的範圍爲-128 ~ 127 ,超事後自動轉化爲對象建立))
	 * 			->  初始化屬性
	 *
	 *
	 *
	 *   Double Check Locking 雙檢查鎖機制(推薦)
	 *    同步塊中不檢查的狀況可能還會屢次建立對象
	 *
	 */
	private volatile static ThreadSafeSingleInstanceModelThree instance = null;

	public static ThreadSafeSingleInstanceModelThree getInstance() {

		if (instance == null) {
			synchronized (ThreadSafeSingleInstanceModelThree.class) {
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				if (instance == null) {  //此處可能對象已經分配指針但沒有初始化  採用 Double Check Locking 雙檢查鎖機制
					instance = new ThreadSafeSingleInstanceModelThree();
				}
			}
		}
		return instance;
	}

}

4.靜態內部類的實現

/**
 * 靜態內部類的單例
 * 沒有獲取實例的時候內部類不會被初始化
 */
public class ThreadSafeSingleInstanceModelFour  {

	private static class ThreadSafeSingleInstanceModelFourInnerClass{
		public static final ThreadSafeSingleInstanceModelFour instance = new ThreadSafeSingleInstanceModelFour();
	}


	public static final ThreadSafeSingleInstanceModelFour getInstance(){
		try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		return ThreadSafeSingleInstanceModelFourInnerClass.instance;
	}

}

5.序列化

public class ThreadSafeSingleInstanceModelSix implements Serializable {

	/**
	  *  序列化能夠保證單例的安全
	 *  但反序列化的過程是readObject() 會建立一個新的對象,readResolve 特性容許你用   建立的實例代替另一個實例,
	 *    該方法忽略了被反序列化的對象,所以返回類初始化建立的那個特殊的實例
	 *    所以 實例的序列 化形式不該該包含任何實際的數據;全部的實例字段都應該被聲明爲 。
	 *    事實上, 若是依賴readResolve 進行實例控制,帶有對象引用類型的全部實例字段都必須聲明爲 transient 。
	 *
	 */
	private volatile static ThreadSafeSingleInstanceModelSix instance = null;

	//標記爲transient
	private transient String testName = "";

	public static ThreadSafeSingleInstanceModelSix getInstance() {

		if (instance == null) {
			synchronized (ThreadSafeSingleInstanceModelSix.class) {
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				if (instance == null) {  //此處可能對象已經分配指針但沒有初始化  採用 Double Check Locking 雙檢查鎖機制
					instance = new ThreadSafeSingleInstanceModelSix();
				}
			}
		}
		return instance;
	}

	private Object readResolve() {
		// Return the one true Elvis and let the garbage collector
		// take care of the Elvis impersonator.
		return instance;
	}

}
如何選用
佔用資源少,不須要延時加載
餓漢式 > 懶漢式

佔用資源大,須要延時加載
懶漢式 > 餓漢式
測試代碼
public class SimpleSluggardSingleInstanceThread extends Thread{
		@Override
		public void run() {
			log.info("Thread getInstance hashCode = {}", SimpleSluggardSingleInstance.getInstance().hashCode());
		}
	}

	public  SimpleSluggardSingleInstanceThread newSimpleSluggardSingleInstanceThread(){
		return new SimpleSluggardSingleInstanceThread();
	}
TestThreads.SimpleSluggardSingleInstanceThread[] threads = new TestThreads.SimpleSluggardSingleInstanceThread[len];
		for (int i = 0; i < threads.length; i++) {
			threads[i] = TestThreads.getThreadHandler().newSimpleSluggardSingleInstanceThread();
		}

		for (int i = 0; i < threads.length; i++) {
			threads[i].start();
		}
相關文章
相關標籤/搜索