Java幾種單例模式的實現與利弊


餓漢式

提前new出來實例了,並不是在第一次調用get方法時才實例化,沒有進行延遲加載

public class Singleton1 {
    private static Singleton1 instance = new Singleton1();

    private Singleton1(){}

    public static Singleton1 getInstance(){
        return instance;
    }
}

懶漢式——非線程安全版本

多線程環境下無法保證單例效果,會多次執行 instance=new Singleton(),需要考慮到多線程

public class Singleton2 {
    private static Singleton2 instance;

    private Singleton2(){}

    public static Singleton2 getInstance(){
        if(instance == null){
            instance = new Singleton2();
        }
        return instance;
    }
}

懶漢式——同步代碼塊版

性能不高,同步范圍太大,在實例化instacne后,獲取實例仍然是同步的,效率太低,需要縮小同步的范圍。

public class Singleton3 {
    private static Singleton3 instance;

    private Singleton3(){}

    public static synchronized Singleton3 getInstance(){
        if(instance == null){
            instance = new Singleton3();
        }
        return instance;
    }
}

懶漢式——同步方法一層check版

縮小同步范圍,來提高性能,但是讓然存在多次執行instance=new Singleton()的可能,由此引出double check

https://www.jianshu.com/p/d53bf830fa09

方法:

  1. 實例方法,鎖住的是實例,public synchronized void method()
  2. 靜態方法,鎖住的是類,public static synchronized void method()

代碼塊:

  1. 實例對象,鎖住的是實例對象,synchronized(this){}
  2. class對象,鎖住的是類對象,synchronized(xxx.class){}
  3. 任意對象, 實例對象的Object,string ss; synchronized(ss){}
public class Singleton4 {
    private static Singleton4 instance;

    private Singleton4(){}

    public static Singleton4 getInstance(){
        if(instance == null){
            synchronized (instance){
                instance = new Singleton4();
            }
        }
        return instance;
    }
}

懶漢式——double check版

避免的上面方式的明顯缺點,但是java內存模型(jmm)並不限制處理器重排序,在執行instance=new Singleton();時,並不是原子語句。

public class Singleton5 {
    private static Singleton5 instance;

    private Singleton5(){}

    public static Singleton5 getInstance(){
        if(instance == null){
            synchronized (instance){
                if(instance == null){
                    instance = new Singleton5();
                }
            }
        }
        return instance;
    }
}

終極版——volatile防指令重排避免多線程出錯

創建一個對象,實際是包括了下面三大步驟:

  1. 為對象分配內存
  2. 初始化實例對象
  3. 把引用instance指向分配的內存空間

這個三個步驟並不能保證按序執行,處理器會進行指令重排序優化,存在這樣的情況:優化重排后執行順序為:1,3,2, 這樣在線程1執行到3時,instance已經不為null了,線程2此時判斷instance!=null,則直接返回instance引用,但現在實例對象還沒有初始化完畢,此時線程2使用instance可能會造成程序崩潰。

public class Singleton6 {
    private static volatile Singleton6 instance;

    private Singleton6(){}

    public static Singleton6 getInstance(){
        if(instance == null){
            synchronized (instance){
                if(instance == null){
                    instance = new Singleton6();
                }
            }
        }
        return instance;
    }
}

懶漢式——靜態內部類

靜態內部類實現的懶漢式.

靜態部分依賴於類,而不是對象,因此會優先於對象加載。類執行的順序:

  1. 靜態屬性、靜態方法聲明、靜態塊
  2. 動態屬性、普通方法聲明、普通代碼塊
  3. 構造方法
public class Singleton7 {
    private Singleton7(){}

    public static Singleton7 getInstance(){
        return InstanceHolder.instance;
    }

    static class InstanceHolder{
        private static Singleton7 instance = new Singleton7();
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM