引言:在網上經常聽到一些牛人談論設計模式的事,自己也只能是個看客,看看別人是如何犀利的。在好奇心的驅使下,我也打算學習學習設計模式,以後也好跟別人交談。下面來看看最基礎的設計模式---單例模式。
單例模式的要點有三個
1.某個類只能有一個實例
2.它必須自行創建這個實例
3.它必須自行向整個系統提供這個實例
從具體實現角度來說就是以下三點
1.單例模式的類只提供私有的構造函數
2.類定義中含有一個該類的靜態私有對象
3.該類提供了一個靜態的共有的函數用於創建或獲取它本身的靜態私有對象
單例模式確保某個類只有一個實例,而且自行實例化並向整個系統提供這個實例。在計算機系統中,線程池、緩存、日誌對象、對話框、打印機、顯卡的驅動程序對象常被設計成單例。這些應用都或多或少具有資源管理器的功能。每臺計算機可以有若干個打印機,但只能有一個Printer
Spooler,以避免兩個打印作業同時輸出到打印機中。每臺計算機可以有若干通信端口,系統應當集中管理這些通信端口,以避免一個通信端口同時被兩個請求同時調用。
看了上面的這段描述,對單例模式的存在價值也知道了個大概。
在網上看到有很多種形式的單例模式,在此我們僅僅只看這三種常用形式:懶漢式單例、餓漢式單例、雙重鎖的式單例。
public class Singleton {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//懶漢式(在第一次調用的時候實例化)
private static Singleton instance=null;
private Singleton(){
}
public static Singleton getSingleton(){
if(instance==null){
System.out.println("aaa");
instance=new Singleton();
}
return instance;
}
//餓漢式(在類初始化時,已經自行實例化)
/*private static Singleton instance=new Singleton();
private Singleton(){
}
public static Singleton getSingleton(){
System.out.println("bbb");
return instance;
}*/
//雙重鎖的形式
/*private static Singleton instance=null;
private Singleton(){
}
public static Singleton getSingleton(){
if(instance==null){
System.out.println("1");
synchronized(Singleton.class){
if(instance==null){
System.out.println("2");
instance=new Singleton();
}
}
}
return instance;
}*/
}
下面來寫一個測試類,看看是不是隻創建了一個實例
public class testSingleton {
public static void main(String[] args) {
Singleton instance1=Singleton.getSingleton();
instance1.setName("111");
System.out.println("instance1 name:"+instance1.getName());
Singleton instance2=Singleton.getSingleton();
instance2.setName("222");
System.out.println("instance2 name:"+instance2.getName());
System.out.println("instance name:"+Singleton.getSingleton().getName());
System.out.println(instance1==instance2);
}
}
打印結果如下:
aaa
instance1 name:111
instance2 name:222
instance name:222
true
由此可以看出,確實只創建了一個實例。
看到這裏不知道大家有沒有發現一個問題,懶漢式和餓漢式都存在線程安全的問題,如果你的進程中同時有多個線程去調這個類的方法,難道還是隻會產生一個實例嗎?下面我們來看看:
public class threadTest implements Runnable {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"====>"+Singleton.getSingleton());
}
}
public static void main(String[] args) {
threadTest t1 = new threadTest();
Thread ta = new Thread(t1, "A");
Thread tb = new Thread(t1, "B");
ta.start();
tb.start();
}
}
打印結果如下:
aaa
A====>test.Singleton@ca0b6
A====>test.Singleton@ca0b6
A====>test.Singleton@ca0b6
A====>test.Singleton@ca0b6
A====>test.Singleton@ca0b6
aaa
B====>test.Singleton@10b30a7
B====>test.Singleton@10b30a7
B====>test.Singleton@10b30a7
B====>test.Singleton@10b30a7
B====>test.Singleton@10b30a7
由此可以看出,多個線程同時調用是實例就並非唯一了,所以就出現了線程不安全的問題了。如果想要它是線程安全的,那麼就可以採用Singleton中雙重鎖的形式。
使用雙重鎖的形式後再來看看打印結果:
1
2
B====>test.Singleton@14318bb
B====>test.Singleton@14318bb
B====>test.Singleton@14318bb
B====>test.Singleton@14318bb
B====>test.Singleton@14318bb
A====>test.Singleton@14318bb
A====>test.Singleton@14318bb
A====>test.Singleton@14318bb
A====>test.Singleton@14318bb
A====>test.Singleton@14318bb
很顯然實例就唯一了。初次接觸,有錯誤的地方還望各位不惜指點,感激不盡!!!