java設計模式單例模式

 建立型模式: – 單例模式、工廠模式、抽象工廠模式、建造者模式、原型模式。java

• 結構型模式: – 適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模 式。spring

• 行爲型模式: – 模版方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模 式、解釋器模式、狀態模式、策略模式、職責鏈模式、訪問者模式數據庫

單例模式:編程

• 核心做用: – 保證一個類只有一個實例,而且提供一個訪問該實例的全局訪問點windows

• 常見應用場景:安全

– Windows的Task Manager(任務管理器)就是很典型的單例模式 – windows的Recycle Bin(回收站)也是典型的單例應用。在整個系統運行過程當中,回收站一直維護着僅有的一個實例。併發

– 項目中,讀取配置文件的類,通常也只有一個對象。沒有必要每次使用配置文件數據,每次new一個對象去讀取。框架

– 網站的計數器,通常也是採用單例模式實現,不然難以同步。jvm

– 應用程序的日誌應用,通常都何用單例模式實現,這通常是因爲共享的日誌文件一直處於打開狀態,由於只能有一個實例去操做 ,不然內容很差追加。ide

– 數據庫鏈接池的設計通常也是採用單例模式,由於數據庫鏈接是一種數據庫資源。

– 操做系統的文件系統,也是大的單例模式實現的具體例子,一個操做系統只能有一個文件系統。

– Application 也是單例的典型應用

– 在Spring中,每一個Bean默認就是單例的,這樣作的優勢是Spring容器能夠管理 – 在servlet編程中,每一個Servlet也是單例 – 在spring MVC框架/struts1框架中,控制器對象也是單例

單例模式的優勢:

– 因爲單例模式只生成一個實例,減小了系統性能開銷,當一個對象的產生須要 比較多的資源時,如讀取配置、產生其餘依賴對象時,則能夠經過在應用啓動 時直接產生一個單例對象,而後永久駐留內存的方式來解決 – 單例模式能夠在系統設置全局的訪問點,優化環共享資源訪問,例如能夠設計 一個單例類,負責全部數據表的映射處理

 常見的五種單例模式實現方式:

– 主要: • 餓漢式(線程安全,調用效率高。 可是,不能延時加載。)

• 懶漢式(線程安全,調用效率不高。 可是,能夠延時加載。)

– 其餘: • 雙重檢測鎖式(因爲JVM底層內部模型緣由,偶爾會出問題。不建議使用)

• 靜態內部類式(線程安全,調用效率高。 可是,能夠延時加載)

• 枚舉單例(線程安全,調用效率高,不能延時加載)

 

餓漢式實現(單例對象當即加載)

//餓漢式單例
public class Singleton01 {
	//類初始化時,當即加載這個對象
	//加載類是自然的線程安全的,(沒有延時加載的優點)
   private static Singleton01 instance=new Singleton01();
   private Singleton01(){
	   
   }
   //方法沒有同步,調用效率高
   public static Singleton01 getInstance(){
	   return instance;
   } 
}

  懶漢式

//懶漢式
public class Singleton02 {
	//類初始化時,不初始化這個對象(真正用到的時候在建立)
    private static Singleton02 instance;
    private Singleton02(){  //私有構造器
    	
    }
    //方法同步,調用效率低
    public static synchronized Singleton02 getInstance(){
    	if(instance==null){
    		instance=new Singleton02();
    	}
    	return instance;
    }
}

  雙重檢索:

//雙重檢測所機制
//因爲編譯器優化緣由和jvm底層內部模型緣由,有時候回出現問題
public class Singleton03 {
	private static Singleton03 instance=null;
	
	public static Singleton03 getInstance(){
		if(instance==null){
			Singleton03 sc;
			synchronized (Singleton03.class){
				sc=instance;
				if(sc==null){
					synchronized(Singleton03.class){
						if(sc==null){
							sc=new Singleton03();
						}
					}
					instance=sc;
				}
			}
		}
		return instance;
	}
	private Singleton03(){
		
	}
}

  靜態內部類:

//靜態內部類
public class Singleton04 {
	 //要點:
	//– 外部類沒有static屬性,則不會像餓漢式那樣當即加載對象。
	//– 只有真正調用getInstance(),纔會加載靜態內部類。加載類時是線程 安全的。 
	//instance是static final 類型,保證了內存中只有這樣一個實例存在,並且只能被賦值一次,
	//從而保證了線程安全性. – 兼備了併發高效調用和延遲加載的優點!

	private static class Singleton04ClassInstance {
		private static final Singleton04 instance = new Singleton04();
	}
	private Singleton04() {

	}
	public static  Singleton04 getInstance() {
		return Singleton04ClassInstance.instance;
	}	
}

  枚舉模式:

//枚舉單例(沒有延時加載)
public enum Singleton05 {
    //定義一個枚舉,枚舉 元素自己就是一個單例
	INSTANCE;
	//添加本身須要的元素
	public void singletonOperation(){
		
	}
}

  

public static void main(String[] args) {
		Singleton01 s1=Singleton01.getInstance();
		Singleton01 s2=Singleton01.getInstance();
		System.out.println(s1);
		System.out.println(s2);
	}

  

 常見的五種單例模式實現方式

– 主要: • 餓漢式(線程安全,調用效率高。 可是,不能延時加載。) • 懶漢式(線程安全,調用效率不高。 可是,能夠延時加載。)

– 其餘: • 雙重檢測鎖式(因爲JVM底層內部模型緣由,偶爾會出問題。不建議使用) • 靜態內部類式(線程安全,調用效率高。 可是,能夠延時加載) • 枚舉式(線程安全,調用效率高,不能延時加載。而且能夠自然的防止反射和反序列 化漏洞!) • 如何選用? – 單例對象  佔用  資源 少,不須要  延時加載: • 枚舉式   好於   餓漢式 – 單例對象  佔用  資源 大,須要 延時加載: • 靜態內部類式   好於  懶漢式

 反射和序列化破壞單例:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;

public class SingletonTest01 {
	public static void main(String[] args) throws Exception{
		Singleton06 s1=Singleton06.getInstance();
		Singleton06 s2=Singleton06.getInstance();
		
		System.out.println(s1);
	    System.out.println(s2);
	    
	    //經過反射的方式直接調用私有構造器
		Class<Singleton06> clazz= (Class<Singleton06>)Class.forName("demo.singleton.Singleton06");
		//獲取無參數構造器
		Constructor<Singleton06> c=clazz.getDeclaredConstructor(null);
		//跳過權限的檢查
		c.setAccessible(true);
		Singleton06 s3=c.newInstance();
		Singleton06 s4=c.newInstance();
		
		System.out.println(s3);
		System.out.println(s4);
		
		//經過序列化的方式構造多個對象
		FileOutputStream fos=new FileOutputStream("d:/a.txt");
		ObjectOutputStream oos=new ObjectOutputStream(fos);
		oos.writeObject(s1);
		oos.close();
		fos.close();
		
		ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d:/a.txt"));
		Singleton06 s5=(Singleton06) ois.readObject(); 
		System.out.println(s5);
	}
}

  

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;

public class SingletonTest01 {
	public static void main(String[] args) throws Exception{
		Singleton06 s1=Singleton06.getInstance();
		Singleton06 s2=Singleton06.getInstance();
		
		System.out.println(s1);
	    System.out.println(s2);
	    
	    //經過反射的方式直接調用私有構造器
		Class<Singleton06> clazz= (Class<Singleton06>)Class.forName("demo.singleton.Singleton06");
		//獲取無參數構造器
		Constructor<Singleton06> c=clazz.getDeclaredConstructor(null);
		//跳過權限的檢查
		c.setAccessible(true);
		Singleton06 s3=c.newInstance();
		Singleton06 s4=c.newInstance();
		
		System.out.println(s3);
		System.out.println(s4);
		
		//經過序列化的方式構造多個對象
		FileOutputStream fos=new FileOutputStream("d:/a.txt");
		ObjectOutputStream oos=new ObjectOutputStream(fos);
		oos.writeObject(s1);
		oos.close();
		fos.close();
		
		ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d:/a.txt"));
		Singleton06 s5=(Singleton06) ois.readObject(); 
		System.out.println(s5);
	}
}

  測試:

import java.util.concurrent.CountDownLatch;

public class SingletonTest03 {
	public static void main(String[] args) throws Exception {
		long start=System.currentTimeMillis();
		int threanNum=10;
		final CountDownLatch countDownLath=new CountDownLatch(threanNum);
				
		
		for(int i=0;i<10;i++){
			new Thread(new Runnable(){
				@Override
				public void run(){
					for(int i=0;i<1000000;i++){
						//Object o=Singleton04.getInstance();
						Object o=Singleton05.INSTANCE;
					}
					countDownLath.countDown();
				}
			}).start();
		}
		countDownLath.await();//mian線程阻塞,知道計數器變成0,纔會繼續往下執行
		long end=System.currentTimeMillis();
		System.out.println("總耗時:"+(end-start));
	}
}
相關文章
相關標籤/搜索