天天看點

【設計模式一之單例模式】java最全單例模式

###java最全單例模式

定義;

確定某一個類隻有一個執行個體,并且自行執行個體化并且向整個系統提供這個執行個體

實作單例模式有幾個比較重要的關鍵點:

  1. 構造的函數一般不對外進行開放,使用private私有屬性
  2. 通過一個靜态方法或者枚舉傳回單例對象
  3. 確定單例類的對象有且隻有一個,尤其是在多線程下,一定要保持線程的安全
  4. 確定單例類對象子反序列化的時候不會重新建構對象

單例模式分類:

  • 餓漢式單例模式
  • 懶漢式單例模式
  • 雙重檢查鎖實作單例
  • 靜态内部類來實作單例模式
  • 使用枚舉類型來實作單例
  • 使用容器的形式來實作單例子模式

餓漢式單例模式

在靜态代碼塊中去new對象, 但是線程不安全,在不使用的時候,其實對象也已經執行個體化了

package com.example.zzf.single;

import java.io.ObjectStreamException;

/**
 * 餓漢式單例模式
 * 在靜态代碼塊中去new對象,
 * 但是線程不安全,在不使用的時候,其實對象也已經執行個體化了
 * @author Administrator
 *
 */
public class SingleTonOne {
	
	private static final SingleTonOne singleTonOne = new SingleTonOne();
	
	private SingleTonOne(){
		
	}
	
	public static SingleTonOne getInstance(){
		return singleTonOne;
	}
	
	public void doSomething(){
		System.out.println("this is SingleTonOne");
	}
	
	/**
	 * 防止單例對象在序列化後,再次反序列化後生成不同的對象
	 * @return
	 * @throws ObjectStreamException
	 */
	private Object readResolve() throws ObjectStreamException{
		return singleTonOne;
	}
}
           

####懶漢式單例模式

優點:在執行個體化的時候去new對象,線程安全

缺點:每一次使用的時候都需要進行同步一次,這樣會過多的消耗資源

package com.example.zzf.single;

import java.io.ObjectStreamException;

/**
 * 懶漢式單例模式
 * 優點:在執行個體化的時候去new對象,線程安全
 * 缺點:每一次使用的時候都需要進行同步一次,這樣會過多的消耗資源
 * @author Administrator
 *
 */
public class SingleTonTwo {
	private static SingleTonTwo singleTonTwo;
	
	private SingleTonTwo(){
		
	}
	
	public static synchronized SingleTonTwo getInstance(){
		if(singleTonTwo == null){
			singleTonTwo = new SingleTonTwo();
		}
		return singleTonTwo;
	}
	
	public void doSomething(){
		System.out.println("this is SingleTonTwo");
	}
	
	/**
	 * 防止單例對象在序列化後,再次反序列化後生成不同的對象
	 * @return
	 * @throws ObjectStreamException
	 */
	private Object readResolve() throws ObjectStreamException{
		return singleTonTwo;
	}
}
           

####雙重檢查鎖實作單例

優點:在需要的時候才去執行個體化,而且能保證線程的安全行,而且單例模式在初始化後,就不會在進行同步,也不會過多的消耗資源

缺點:第一次在加載的時候會加載稍微慢一點,雙重檢查鎖失效可能會出現單例實作有問題

package com.example.zzf.single;

import java.io.ObjectStreamException;

/**
 * 雙重檢查鎖實作單例
 * 有點:在需要的時候才去執行個體化,而且能保證線程的安全行,而且單例模式在初始化後,就不會在進行同步,也不會過多的消耗資源
 * 缺點:第一次在加載的時候會加載稍微慢一點
 * @author Administrator
 *
 */
public class SingleTonThree {
	private static SingleTonThree sInstance = null;
//	private static volatile  SingleTonThree sInstance = null; //從主記憶體中讀取,會影響性能
	
	private SingleTonThree(){
		
	}
	
	public static SingleTonThree getInstance(){
		if(sInstance == null){
			synchronized (SingleTonThree.class) {
				if(sInstance == null){
					sInstance = new SingleTonThree();
				}
				return sInstance;
			}
		}
		return sInstance;
	}
	
	public void doSomething(){
		System.out.println("this is SingleTonThree");
	}
	
	/**
	 * 防止單例對象在序列化後,再次反序列化後生成不同的對象
	 * @return
	 * @throws ObjectStreamException
	 */
	private Object readResolve() throws ObjectStreamException{
		return sInstance;
	}
	
}
           

靜态内部類來實作單例模式

由于雙重檢查鎖在高并發或者因為記憶體模型的原因,有時候偶爾會失效,是以推薦使用靜态内部類來實作單例模式

第一次加載的時候不會初始化SingleTonFour,隻有第一次調用getInstance的時候才會去建立對象,

既保證了線程安全性麼也保證對象唯一性,同時延遲了單例的執行個體化

package com.example.zzf.single;

import java.io.ObjectStreamException;

/**
 * 靜态内部類來實作單例莫斯
 * 由于雙重檢查鎖在高并發或者因為記憶體模型的原因,有時候偶爾會失效,是以推薦使用靜态内部類來實作單例模式
 * 第一次加載的時候不會初始化SingleTonFour,隻有第一次調用getInstance的時候才會去建立對象,
 * 既保證了線程安全性麼也保證對象唯一性,同僚延遲了單利的執行個體化
 * @author Administrator
 *
 */
public class SingleTonFour {
	private SingleTonFour(){
		
	}
	
	public static SingleTonFour getInstance(){
		return SingleTonFourHolder.singleTonFour;
	}
	
	private static class SingleTonFourHolder{
		private static final SingleTonFour singleTonFour = new SingleTonFour();
	}
	
	public void doSomething(){
		System.out.println("this is SingleTonFour");
	}
	
	/**
	 * 防止單例對象在序列化後,再次反序列化後生成不同的對象
	 * @return
	 * @throws ObjectStreamException
	 */
	private Object readResolve() throws ObjectStreamException{
		return SingleTonFourHolder.singleTonFour;
	}
}
           

####使用枚舉類型來實作單例

好處:枚舉在java中就和普通的類型是一樣的,不僅能夠有字段,還可以有自己的方法,最主要的就是枚舉類型執行個體的建立預設是線程安全的,在任何一種情況下他都隻會有一個執行個體

package com.example.zzf.single;

/**
 * 使用枚舉類型來實作單例
 * 好處:枚舉在java中就和普通的類型是一樣的,不僅能夠有字段,還可以有自己的方法,最主要的就是枚舉類型執行個體的建立預設是線程安全的,在任何一種情況下他都隻會有一個執行個體
 * 
 * @author Administrator
 *
 */
public enum SingleTonEnum {
	INSTANCE;
	public void doSomething(){
		System.out.println("this is SingleTonEnum!");
	}
}

           

####使用容器的形式來實作單例子模式

将多個單例類型全部注入到一個管理類中進行統一管理,根據需求來選擇

package com.example.zzf.single;

/**
 * 使用容器的形式來實作單例子模式
 * 将多個單例類型全部注入到一個管理類中進行統一管理,根據需求來選擇
 * 
 */
import java.util.HashMap;
import java.util.Map;

public class SingleTonManager {
	private static Map<String,Object> objectMap = new HashMap();
	
	private SingleTonManager(){
		
	}
	
	public static  void registerSingleServer(String key,Object instance){
		if(!objectMap.containsKey(key)){
			objectMap.put(key, instance);
		}
	}
	
	public static Object getSingleService(String key){
		return objectMap.get(key);
	}
	
}
           

MainTest.java中的代碼:

package com.example.zzf;

import com.example.zzf.single.SingleTonEnum;
import com.example.zzf.single.SingleTonFour;
import com.example.zzf.single.SingleTonManager;
import com.example.zzf.single.SingleTonOne;
import com.example.zzf.single.SingleTonThree;
import com.example.zzf.single.SingleTonTwo;

public class MainTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SingleTonOne singleTonOne = SingleTonOne.getInstance();
		singleTonOne.doSomething();
		SingleTonManager.registerSingleServer("SingleTonOne", singleTonOne);
		
		SingleTonTwo singleTonTwo = SingleTonTwo.getInstance();
		singleTonTwo.doSomething();
		SingleTonManager.registerSingleServer("SingleTonTwo", singleTonTwo);
		
		SingleTonThree singleTonThree = SingleTonThree.getInstance();
		singleTonThree.doSomething();
		SingleTonManager.registerSingleServer("SingleTonThree", singleTonThree);
		
		SingleTonFour singleTonFour = SingleTonFour.getInstance();
		singleTonFour.doSomething();
		SingleTonManager.registerSingleServer("SingleTonFour", singleTonFour);
		
		SingleTonEnum singleEnum = SingleTonEnum.INSTANCE;
		singleEnum.doSomething();
		SingleTonManager.registerSingleServer("SingleTonEnum", singleEnum);
		
		System.out.println("===========================================");
		singleTonOne = (SingleTonOne) (SingleTonManager.getSingleService("SingleTonOne"));
		singleTonTwo = (SingleTonTwo) SingleTonManager.getSingleService("SingleTonTwo");
		singleTonThree = (SingleTonThree) SingleTonManager.getSingleService("SingleTonThree");
		singleTonFour = (SingleTonFour) SingleTonManager.getSingleService("SingleTonFour");
		singleEnum =	(SingleTonEnum) SingleTonManager.getSingleService("SingleTonEnum");
		singleTonOne.doSomething();
		singleTonTwo.doSomething();
		singleTonThree.doSomething();
		singleTonFour.doSomething();
		singleEnum.doSomething();		
	}
}
           

這些都是一些很簡單的了解和總結,希望這些能夠幫助到大家:

源碼下載下傳位址:源代碼下載下傳