意圖:確保一個類只有一個實例,並提供一個全局訪問點
適用性:
1、當類只能有一個實例而且客戶可以從一個衆所周知的訪問點訪問它時
2、當這個唯一實例應該是通過子類化可擴展的,並且客戶應該無需更改代碼就能使用一個擴展的實例時
結構圖:
實例:
public class Singleton {
private static Singleton instance;
private String message;
private Singleton(){
message ="message被初始化";
System.out.println(System.currentTimeMillis());
}
public static Singleton getInstance() {
if(instance == null){
instance = new Singleton();
}
return instance;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
public class Client {
public static void main(String args[]){
Singleton singleton = Singleton.getInstance();
System.out.println(singleton.getMessage());
//第二次獲取
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton2.getMessage());
}
}
輸出:
1362036694546
message被初始化
message被初始化
從輸出可以看出,第二次獲取時,沒有新建實例
考慮到併發,在getInstance()方法加上synchronized關鍵字修飾,但可能會使性能下降,可以有以下兩種方式
1、使用'餓漢式'創建實例,而不用'懶漢式'的做法
public class Singleton {
private static Singleton instance = new Singleton();
private String message;
private Singleton(){
message ="message被初始化";
System.out.println(System.currentTimeMillis());
}
public static Singleton getInstance() {
return instance;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
利用這個做法,依賴JVM在加載這個類時馬上創建此唯一的單件實例,JVM保證在任何線程訪問instance靜態變量之前,一定先創建此實例
2、利用雙重檢查加鎖,在getInstance()中減少使用同步,優化'懶漢式'初始化
public class Singleton {
private volatile static Singleton instance;
private String message;
private Singleton(){
message ="message被初始化";
System.out.println(System.currentTimeMillis());
}
public static Singleton getInstance() {
if(instance == null){
synchronized (Singleton.class) {
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
當關注性能時,這樣的做法可以大大減少getInstance()耗費的時間
注意點:
1、單件模式需要一個私有的構造器、一個靜態方法、一個靜態變量
2、確定在性能和資源上的限制,然後小心的選擇適當的方案以解決多線程的問題