概念:html
java中單例模式是一種常見的設計模式,單例模式分三種:懶漢式單例、餓漢式單例、登記式單例三種。
單例模式有一下特色:
1、單例類只能有一個實例。
2、單例類必須本身本身建立本身的惟一實例。
3、單例類必須給全部其餘對象提供這一實例。
單例模式確保某個類只有一個實例,並且自行實例化並向整個系統提供這個實例。在計算機系統中,線程池、緩存、日誌對象、對話框、打印機、顯卡的驅動程序對象常被設計成單例。這些應用都或多或少具備資源管理器的功能。每臺計算機能夠有若干個打印機,但只能有一個Printer Spooler,以免兩個打印做業同時輸出到打印機中。每臺計算機能夠有若干通訊端口,系統應當集中管理這些通訊端口,以免一個通訊端口同時被兩個請求同時調用。總之,選擇單例模式就是爲了不不一致狀態,避免政出多頭。java
首先看一個經典的單例實現。設計模式
public class Singleton {緩存
private static Singleton uniqueInstance = null;安全
private Singleton() {併發
// Exists only to defeat instantiation.app
}ide
public static Singleton getInstance() {post
if (uniqueInstance == null) {this
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// Other methods...
}
Singleton經過將構造方法限定爲private避免了類在外部被實例化,在同一個虛擬機範圍內,Singleton的惟一實例只能經過getInstance()方法訪問。(事實上,經過Java反射機制是可以實例化構造方法爲private的類的,那基本上會使全部的Java單例實現失效。此問題在此處不作討論,姑且掩耳盜鈴地認爲反射機制不存在。)
可是以上實現沒有考慮線程安全問題。所謂線程安全是指:若是你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。若是每次運行結果和單線程運行的結果是同樣的,並且其餘的變量的值也和預期的是同樣的,就是線程安全的。或者說:一個類或者程序所提供的接口對於線程來講是原子操做或者多個線程之間的切換不會致使該接口的執行結果存在二義性,也就是說咱們不用考慮同步的問題。顯然以上實現並不知足線程安全的要求,在併發環境下極可能出現多個Singleton實例。
1 public class TestStream { 2 private String name; 3 public String getName() { 4 return name; 5 } 6 public void setName(String name) { 7 this.name = name; 8 } 9 //該類只能有一個實例 10 private TestStream(){} //私有無參構造方法 11 //該類必須自行建立 12 //有2種方式 13 /*private static final TestStream ts=new TestStream();*/ 14 private static TestStream ts1=null; 15 //這個類必須自動向整個系統提供這個實例對象 16 public static TestStream getTest(){ 17 if(ts1==null){ 18 ts1=new TestStream(); 19 } 20 return ts1; 21 } 22 public void getInfo(){ 23 System.out.println("output message "+name); 24 } 25 }
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 //靜態工廠方法 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 //靜態工廠方法
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 //靜態工廠方法,返還此類唯一的實例 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 }