一、單態定義:
Singleton 模式主要作用是保證在 Java 應用程序中,一個類 Class 只有一個實例存在。
二. 單例模式的特點
1. 單例模式只能有一個實例。
2. 單例類必須創建自己的唯一實例。
3. 單例類必須向其他對象提供這一實例。
在很多操作中,比如建立目錄數據庫連接都需要這樣的單線程操作。
還有, singleton 能夠被狀態化; 這樣,多個單態類在一起就可以作為一個狀態倉庫一樣向外提供服務,比如,你要論壇中的帖子計數器,每次瀏覽一次需要計數,單態類能否保持住這個計數,並且能 synchronize 的安全自動加 1,如果你要把這個數字永久保存到數據庫,你可以在不修改單態接口的情況下方便的做到。
另外方面,Singleton 也能夠被無狀態化。提供工具性質的功能,Singleton 模式就為我們提供了這樣實現的可能。使用 Singleton 的好處還在於可以節省內存,因為它限制了實例的個數,有利於 Java 垃圾回收(garbage collection)。我們常常看到工廠模式中類裝入器(class loader)中也用 Singleton 模式實現的,因為被裝入的類實際也屬於資源。
如何使用?
第一種形式:
public class Singleton {
private Singleton(){}
//注意這是 private 只供內部調用
private static Singleton instance = new Singleton();
//這裡提供了一個供外部訪問本 class 的靜態方法,可以直接訪問
public static Singleton getInstance() {
return instance;
}
}
第二種形式:
public class Singleton {
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
//這個方法比上面有所改進,不用每次都進行生成對象,只是第一次
//使用時生成實例,提高了效率!
if (instance==null)
instance=new Singleton();
return instance; }
}
使用 Singleton.getInstance()可以訪問單態類。
第三種形式:
public class SingletonDemo {
private static SingletonDemo instance;
private SingletonDemo(){
}
public static synchronized SingletonDemo getInstance(){
if(instance==null){
instance=new SingletonDemo();
}
return instance;
}
}
第三種形式中的 synchronized,這個 synchronized 很重要,如果沒有 synchronized,一個是考慮了線程安全,再就是如果不加的話 getInstance()是有可能得到多個 Singleton 實例。
第四種形式:
public class SingletonDemo {
private static class SingletonHolder{
private static SingletonDemo instance=new SingletonDemo();
}
private SingletonDemo(){
System.out.println("Singleton has loaded");
}
public static SingletonDemo getInstance(){
return SingletonHolder.instance;
}
}
第四種形式使用內部類的好處是,靜態內部類不會在單例加載時就加載,而是在調用getInstance()方法時才進行加載,達到了類似懶漢模式的效果,而這種方法又是線程安全的。
第五種形式:
public class SingletonDemo {
private volatile static SingletonDemo instance;
private SingletonDemo(){
System.out.println("Singleton has loaded");
}
public static SingletonDemo getInstance(){
if(instance==null){
synchronized (SingletonDemo.class){
if(instance==null){
instance=new SingletonDemo();
}
}
}
return instance;
}
}
第五種形式叫雙重校驗鎖法,下面是這種形式的使用場景。
STEP 1. 線程A訪問getInstance()方法,因為單例還沒有實例化,所以進入了鎖定塊。
STEP 2. 線程B訪問getInstance()方法,因為單例還沒有實例化,得以訪問接下來代碼塊,而接下來代碼塊已經被線程1鎖定。
STEP 3. 線程A進入下一判斷,因為單例還沒有實例化,所以進行單例實例化,成功實例化後退出代碼塊,解除鎖定。
STEP 4. 線程B進入接下來代碼塊,鎖定線程,進入下一判斷,因為已經實例化,退出代碼塊,解除鎖定。
STEP 5. 線程A獲取到了單例實例並返回,線程B沒有獲取到單例並返回Null。
理論上雙重校驗鎖法是線程安全的
Singleton 模式看起來簡單,使用方法也很方便,但是真正用好,是非常不容易,需要對 Java 的類 線程 內存等概念有相當的瞭解。
總之:如果你的應用基於容器,那麼 Singleton 模式少用或者不用,可以使用相關替代技術。
閱讀更多 老孔說編程 的文章