java多線程 —— 單例模式

目錄

1、什麼是單例?

2、爲什麼使用單例?

3、單例模式怎麼創建


1、什麼是單例?

    單例模式實際上是一種設計模式。它達到的效果是,在程序的運行過程中,一個類最多隻會被實例化(初始化)一次。

2、爲什麼使用單例?

    上面我們已經知道單例模式,其實就是一個類的變量和方法最多隻會被初始化一次,即全局唯一。全局唯一可以節省內存開銷,提供全局唯一的變量和方法等等優點,但是其實靜態變量和靜態方法也是能達到全局唯一的效果,那麼爲什麼一定要用單例呢?

    其實,在我看來,單例可以達到的效果也可以用靜態變量和靜態方法的方式而實現。單例模式的存在意義,有兩點:

  1. 面向對象的編程方式。何爲面向對象:https://blog.csdn.net/jjs15259655776/article/details/77507529
  2. 可以控制實例的數量,進行有意義的派生。
  3. 多線程的情況下,如果靜態方法使用了一個靜態字段,這個靜態字段可以會被多個線程修改,因此說如果在靜態方法裏使用了靜態變量,這就會有線程安全問題

    可以這麼說,靜態變量和靜態方法的方式是基於對象,單例模式是面向對象的。

例如

  1. 數據庫連接池。數據庫連接池不僅有連接池的配置、屬性,還有獲取連接的各種方法。顯然,這是一種面向對象的編程方式。又因爲數據庫連接池在大多數設計上面都是全局唯一的,所以數據庫連接池經常被設計爲單例。
  2. 系統運行時的配置、屬性、方式。這種場景也經常設置爲單例。
  3. 一些工具類,提供公共的方法實現一些特定的功能,這時候就可以用靜態方法。

3、單例模式怎麼創建

    由於大部分(有價值的)程序都是多線程的。我這邊只提供線程安全的單例模式,當然線程不安全的單例模式其實是錯誤的單例模式。

  1. 餓漢模式:
  • 即利用靜態變量的特點,在類加載的時候就初始化SingleTon1,以達到線程安全的單例模型。
  • 但是,沒有懶加載的效果,如果你的單例是比較大的,不建議使用餓漢模式。
/**
 * 這種叫餓漢模式。
 * 即利用靜態變量的特點,在類加載的時候就初始化SingleTon1,已達到線程安全的單例模型。
 */
public class SingleTon1 {

    private static SingleTon1 instance = new SingleTon1();//利用靜態變量的特點,在類加載的時候就初始化SingleTon1

    private SingleTon1() {//私有化構造函數,這樣在其他類就不能夠初始化類
    }

    public static SingleTon1 getInstance() {//獲取的是同一個instance
        return instance;
    }

}
public class Singleton {  
    private Singleton instance = null;  
    static {  
    instance = new Singleton();  //利用靜態代碼塊,在類加載的時候初始化,其實跟上面是一樣的,算是一種餓漢模式的變種
    }  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return this.instance;  
    }  
}  

2、雙重檢驗鎖

  • 達到懶加載的效果,而不是類加載過程中就初始化,達到節省內存的效果
  • volatile。用volatile修飾instance是必要的,不修飾的話,有可能因爲延遲初始化的問題,導致系統崩潰。具體的,是因爲java的初始化是無序的,如果不用volatile修飾instance,保證instance的可見性,有可能一個線程得到的instance是沒有初始化完成的。即一個線程拿到的是還會初始化的對象,這時候會造成系統崩潰。
  • 雙重檢驗null的原因。一個原因是提升效率,一個是達到線程安全的目的。具體的需要結合代碼一步一步看,這裏就不展開了。

public class SingleTon2 {
    private static volatile SingleTon2 instance;
    private SingleTon2(){}
    public static SingleTon2 getInstance(){
        if (instance == null){
            synchronized (SingleTon2.class){
                instance = new SingleTon2();
            }
        }
        return instance;
    }
}

這裏值得一提的是枚舉的方式實現單例。

  • 枚舉的方式除了能保證線程安全以外,還有一個其他實現方式無法比擬的有點,就是枚舉的實現方式支持序列化和反序列化
package singleTon;


public enum SingleTon3 {
    INSTANCE;
    public void test(){
        System.out.println("枚舉型單例");
    }
}

class Test{
    public static void main(String[] args) {
        SingleTon3.INSTANCE.test();
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章