1. 程式人生 > >建立型模式之單例模式(2.1)

建立型模式之單例模式(2.1)

單例模式定義

在軟體系統中,一個類只有一個例項物件。(該類只提供一個取得例項的靜態方法)


推薦使用的三種單例模式

  • DoubleCheck
  • 靜態內部類
  • 列舉


1.DoubleCheck 雙重檢查



特點:效率高,執行緒安全,延遲載入。


class DoubleCheck
{
    private static volatile DoubleCheck instance;

    private DoubleCheck(){}

    public static DoubleCheck getInstance()
    {
        /*
        DoubleCheck如何實現?執行緒安全和效率提升
        
        在多執行緒的環境下,假設執行緒A直接進入#2,例項化物件。
        且例項化方法外用synchronized修飾,所以是執行緒安全的。
        當執行緒A例項化物件結束,物件instance已經被建立,執行到#1的執行緒將會直接調到#3,返回instance
        
        且DoubleCheck實現了延遲載入(new在方法裡)
         */
        if(instance==null)                               //#1
        {
            synchronized (DoubleCheck.class)             //#2
            {
                if(instance==null)
                {
                    instance = new DoubleCheck();        //#3
                }
            }
        }
        return instance;
    }
}


public class Operation
{
    public static void main(String[] args) {
        DoubleCheck doubleCheck1 = DoubleCheck.getInstance();
        DoubleCheck doubleCheck2 = DoubleCheck.getInstance();

        System.out.println(doubleCheck1.hashCode());
        System.out.println(doubleCheck2.hashCode());


    }
}

2.靜態內部類

特點:通過JVM類載入避免了執行緒安全問題,延遲載入,效率高。

class StaticClassInner {

    private StaticClassInner() {}



    /*
     使用靜態內部類,實現了延遲載入
     呼叫getInstance()方法時,才會載入StaticClassInnerInstance。

     通過JVM類載入執行緒安全的機制,避免了執行緒不安全。

     */
    private static class StaticClassInnerInstance {
        private static final StaticClassInner INSTANCE = new StaticClassInner();
    }

    public static StaticClassInner getInstance() {
        return StaticClassInnerInstance.INSTANCE;
    }
}

public class Operation
{
    public static void main(String[] args) {
        StaticClassInner doubleCheck1 = StaticClassInner.getInstance();
        StaticClassInner doubleCheck2 = StaticClassInner.getInstance();

        System.out.println(doubleCheck1.hashCode());
        System.out.println(doubleCheck2.hashCode());


    }
}

3.列舉

Effective Java作者Josh Bloch推薦。

enum Hq
{
    INSTANCE;

    public void printf()
    {
        System.out.println("ins");
    }
}

public class Operation
{
    public static void main(String[] args) {
        Hq hq = Hq.INSTANCE;
        Hq hqq = Hq.INSTANCE;

        System.out.println(hq.hashCode());
        System.out.println(hqq.hashCode());
    }
}


餓漢式的延遲載入問題(可用)



如果建立的物件一定會被使用,那麼可以忽略記憶體浪費的問題。



class SingleTom
{
    private SingleTom()
    { }

    /*
    在靜態常量中例項化物件,無法實現延遲載入,如果物件未被使用,會造成記憶體浪費。(#1)
    無執行緒安全問題

    將例項化物件放於靜態程式碼塊中並無實際作用
     */
    private final static SingleTom instance;  // = new SingleTom();#1

    static  //#2
    {
        instance=new SingleTom();
    }


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

public class Operation
{
    public static void main(String[] args) {
        SingleTom hq = SingleTom.getInstance();
        SingleTom hqq = SingleTom.getInstance();

        System.out.println(hq.hashCode());
        System.out.println(hqq.hashCode());
    }
}


懶漢式的執行緒安全問題(不可用)


class Singletom {

    private static Singletom singleton;

    private Singletom() {}

    /*
    執行緒不安全:

    呼叫getInsyance()方法時,如果同時有多個執行緒同時進入到#1
    就會建立多個例項物件

    倘若在方法上加上syn關鍵字,執行緒同步問題解決,但效率大大降低
    
    doublecheck大概就是在這種糾結下選擇用兩次if(singleton == null)來控制執行緒同步和效率問題

     */
    public static /*synchronized*/ Singletom getInstance() {
        if (singleton == null) {                   //#1
            singleton = new Singletom();
        }
        return singleton;
    }
}

public class Operation
{
    public static void main(String[] args) {
        Singletom hq = Singletom.getInstance();
        Singletom hqq = Singletom.getInstance();

        System.out.println(hq.hashCode());
        System.out.println(hqq.hashCode());
    }
}

參考文件:

單例模式的八種寫法比