Java學習筆記--單例

一、什么是單例模式?

單例模式(Singleton Pattern),顧名思義,就是被單例的對象只能有一個實例存在。單例模式的實現方式是,一個類能返回對象的一個引用(永遠是同一個)和一個獲得該唯一實例的方法(必須是靜態(tài)方法)。通過單例模式,我們可以保證系統(tǒng)中只有一個實例,從而在某些特定的場合下達到節(jié)約或者控制系統(tǒng)資源的目的。

二、代碼如何實現?

  1. 餓漢模式
    最常見、最簡單的單例模式寫法之一,即在類的一開始就給它新建一個實例。示例如下:
//餓漢模式
class Singleton {
    
    //一開始就創(chuàng)建一個實例
    private static Singleton instance = new Singleton();
    
    private Singleton() { }
    
    //獲取實例
    public static Singleton getInstance() {
        return instance;
    }
}

存在的問題:這種方式有一個明顯的缺點,那就是不管有沒有調用過獲得實例的方法,每次都會新建一個實例。

  1. 懶漢模式
    餓漢模式的升級版本,即在類的一開始只創(chuàng)建一個引用,但并不實例化,只有在需要使用到它的時候,先去判斷實例是否為空,如果為空的時候才會新建一個實例來使用。示例如下:
//懶漢模式
class Singleton {

    //一開始并不創(chuàng)建實例
    private static Singleton instance;

    private Singleton() { }

    //需要時再新建
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

存在的問題:該種模式存在一個嚴重的問題。那就是如果有多個線程并行調用getInstance()的時候,還是會創(chuàng)建多個實例的,單例模式就失效了。

  1. 線程安全的懶漢模式
    在懶漢模式的基礎上,把它設為線程同步(synchronized)就好了。synchronize的作用就是保證在同一時刻最多只有一個線程運行,這樣就避免了多線程帶來的問題。示例代碼如下:
//懶漢模式(線程安全)
class Singleton {
    
    private static Singleton instance;

    private Singleton() { }

    //添加了 synchronized 關鍵字
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

存在的問題:這種模式看似很好的解決了多線程的問題,但是它的效率并不高,每次調用獲得實例的方法時都要進行同步,但是多數情況下并不需要同步操作。

  1. 雙重檢驗鎖
    為了解決第三種模式的問題,延伸出來雙重檢驗鎖模式,即在獲取實例的方法中,先去判斷該實例是否為空,如果為空在再加同步鎖,然后新建實例。示例如下:
//雙重檢驗鎖
class Singleton {

    private static Singleton instance;

    private Singleton() { }
    
    public static Singleton getInstance() {
        //第一個檢驗鎖,如果不為空直接返回實例對象,為空才進入下一步
        if (instance == null) {
            //第二個檢驗鎖,因為可能有多個線程進入到if語句內,所以加線程同步鎖
            synchronized (Singleton.class) { 
                instance = new Singleton();   
            }
        }
        return instance;
    }
}

存在的問題:該種模式此時并不是完美的。主要問題在在于instance = new Singleton();這句代碼,因為JVM(Java虛擬機)執(zhí)行這句代碼的時候,要做好幾件事情,而JVM為了優(yōu)化代碼,有可能造成做這幾件事情的執(zhí)行順序是不固定的,從而造成錯誤。

解決辦法:為了解決上述問題,我們需要給實例加一個volatile關鍵字,它的作用就是防止編譯器自行優(yōu)化代碼。此時,雙重檢驗鎖模式才完美實現。

private volatile static Singleton instance;
  1. 靜態(tài)內部類
    這種方式,利用了 JVM 自身的機制來保證線程安全,因為 SingletonHolder 類是私有的,除了 getInstance() 之外沒有其它方式可以訪問實例對象,而且只有在調用 getInstance() 時才會去真正創(chuàng)建實例對象。示例如下:
//靜態(tài)內部類
class Singleton {

    private static class SingletonHolder {
        private static final Singleton singleton = new Singleton();
    }
    
    private Singleton() { }

    public static Singleton getInstance() {
        return SingletonHolder.singleton;
    }
}
  1. 枚舉
    我們可以通過 Wife.INSTANCE 來訪問實例對象,而且創(chuàng)建枚舉默認就是線程安全的,還可以防止反序列化帶來的問題。最為推薦。示例如下:
//利用枚舉的方式創(chuàng)建單例
public enum Singleton {
    INSTANCE;

    public void whateverMethod() {
        
    }
}
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容