Java 單例模式詳解
概念:
java中單例模式是一種常見的設計模式,單例模式分三種:懶漢式單例、餓漢式單例、登記式單例三種。
單例模式有一下特點:
1、單例類只能有一個執行個體。
2、單例類必須自己自己建立自己的唯一執行個體。
3、單例類必須給所有其他對象提供這一執行個體。
單例模式確保某個類只有一個執行個體,而且自行執行個體化並向整個系統提供這個執行個體。在電腦系統中,線程池、緩衝、日誌對象、對話方塊、印表機、顯卡的驅動程式對象常被設計成單例。這些應用都或多或少具有資源管理員的功能。每台電腦可以有若干個印表機,但只能有一個Printer
Spooler,以避免兩個列印工作同時輸出到印表機中。每台電腦可以有若干通訊連接埠,系統應當集中管理這些通訊連接埠,以避免一個通訊連接埠同時被兩個請求同時調用。總之,選擇單例模式就是為了避免不一致狀態,避免政出多頭。
首先看一個經典的單例實現。
public
class Singleton {
private
static Singleton uniqueInstance =null;
private Singleton() {
// Exists only to defeat instantiation.
}
public
static Singleton getInstance() {
if (uniqueInstance ==null)
{
uniqueInstance =new Singleton();
}
return uniqueInstance;
}
// Other methods...
}
Singleton通過將構造方法限定為private避免了類在外部被執行個體化,在同一個虛擬機器範圍內,Singleton的唯一執行個體只能通過getInstance()方法訪問。(事實上,通過Java反射機制是能夠執行個體化構造方法為private的類的,那基本上會使所有的Java單例實現失效。此問題在此處不做討論,姑且掩耳盜鈴地認為反射機制不存在。)
但是以上實現沒有考慮安全執行緒問題。所謂安全執行緒是指:如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程啟動並執行結果是一樣的,而且其他的變數的值也和預期的是一樣的,就是安全執行緒的。或者說:一個類或者程式所提供的介面對於線程來說是原子操作或者多個線程之間的切換不會導致該介面的執行結果存在二義性,也就是說我們不用考慮同步的問題。顯然以上實現並不滿足安全執行緒的要求,在並發環境下很可能出現多個Singleton執行個體。
package com.test;
public class Singleton {
/** * @param args */ private String name; private static Singleton sleton = null; public String getName() { return name; }
public void setName(String name) { this.name = name; } public Singleton(){ } public synchronized static Singleton getInstance(){ if(null == sleton) sleton = new Singleton(); return sleton; } public void getCurrentName(){ System.out.println("當前對象的name值:"+name); }
public static void main(String[] args) { // TODO Auto-generated method stub Singleton instance1 = Singleton.getInstance(); instance1.setName("周潤發"); System.out.println(instance1.getName()); Singleton instance2 = Singleton.getInstance(); instance2.setName("劉德華"); System.out.println(instance1.getName()); if(instance1 == instance2) System.out.println("instance1\2為同一個執行個體"); else System.out.println("instance1\2不為同一個執行個體"); }
}
1 public class TestMain { 2 public static void main(String [] args){ 3 TestStream s=TestStream.getTest(); 4 s.setName("張孝祥"); 5 System.out.println(s.getName()); 6 TestStream s1=TestStream.getTest(); 7 s1.setName("張孝祥"); 8 System.out.println(s1.getName()); 9 s.getInfo();10 s1.getInfo();11 if(s==s1){12 System.out.println("建立的是同一個執行個體");13 }else if(s!=s1){14 System.out.println("建立的不是同一個執行個體");15 }else{16 System.out.println("application error");17 }18 }19 }
運行結果:
張孝祥
張孝祥
output message 張孝祥
output message 張孝祥
建立的是同一個執行個體
結論:由結果可以得知單例模式為一個物件導向的應用程式提供了對象惟一的訪問點,不管它實現何種功能,整個應用程式都會同享一個執行個體對象。
1.餓漢式單例類
1 //餓漢式單例類.在類初始化時,已經自行執行個體化 2 public class Singleton1 { 3 //私人的預設構造子 4 private Singleton1() {} 5 //已經自行執行個體化 6 private static final Singleton1 single = new Singleton1(); 7 //靜態Factory 方法 8 public static Singleton1 getInstance() { 9 return single;10 }11 }
2.懶漢式單例類
1 //懶漢式單例類.在第一次調用的時候執行個體化 2 public class Singleton2 { 3 //私人的預設構造子 4 private Singleton2() {} 5 //注意,這裡沒有final 6 private static Singleton2 single=null; 7 //靜態Factory 方法 8 public synchronized static Singleton2 getInstance() { 9 if (single == null) { 10 single = new Singleton2();11 } 12 return single;13 }14 }
3.登記式單例類
1 import java.util.HashMap; 2 import java.util.Map; 3 //登記式單例類. 4 //類似Spring裡面的方法,將類名註冊,下次從裡面直接擷取。 5 public class Singleton3 { 6 private static Map<String,Singleton3> map = new HashMap<String,Singleton3>(); 7 static{ 8 Singleton3 single = new Singleton3(); 9 map.put(single.getClass().getName(), single);10 }11 //保護的預設構造子12 protected Singleton3(){}13 //靜態Factory 方法,返還此類惟一的執行個體14 public static Singleton3 getInstance(String name) {15 if(name == null) {16 name = Singleton3.class.getName();17 System.out.println("name == null"+"--->name="+name);18 }19 if(map.get(name) == null) {20 try {21 map.put(name, (Singleton3) Class.forName(name).newInstance());22 } catch (InstantiationException e) {23 e.printStackTrace();24 } catch (IllegalAccessException e) {25 e.printStackTrace();26 } catch (ClassNotFoundException e) {27 e.printStackTrace();28 }29 }30 return map.get(name);31 }32 //一個示意性的商業方法33 public String about() { 34 return "Hello, I am RegSingleton."; 35 } 36 public static void main(String[] args) {37 Singleton3 single3 = Singleton3.getInstance(null);38 System.out.println(single3.about());39 }40 }