單例模式(懶漢模式)

package com.exam.test;

/**
 * 懶漢式單例
 * Created by xiangyang.laixiang on 2016/8/23.
 */
public class SingleInstance {
    private static SingleInstance singleInstance;

    /**
     * 懶漢式單例,非線程安全
     * @return
     */
    public static SingleInstance getSingleInstance(){
        if(singleInstance == null)
        {
            singleInstance = new  SingleInstance();
        }
        return singleInstance;
    }

    /**
     * 懶漢式單例,線程安全
     * 這是實現線程安全最簡單的方式,但這里會導致一個性能問題,因為我們把整個getInstance同步起來了,
     * 這就會導致每時每刻都只能有一個線程調用getInstance方法,而同步只發生在第一次聲明的時候
     * 這就引出了雙重檢驗鎖的概念
     * @return
     */
    public synchronized static SingleInstance getSingleInstance2(){
        if(singleInstance == null)
        {
            singleInstance = new  SingleInstance();
        }
        return singleInstance;
    }
}


/**
 * 雙重檢驗鎖
 */
class DoubleCheckSingleInstance{
    //使用volatile關鍵字修飾
    //private static DoubleCheckSingleInstance singleInstance;
    private static DoubleCheckSingleInstance singleInstance;
    public static DoubleCheckSingleInstance getSingleInstance()
    {
        if(singleInstance == null)
        {
            synchronized (DoubleCheckSingleInstance.class)
            {
                if(singleInstance == null)
                {
                    singleInstance = new DoubleCheckSingleInstance();
                }
            }
        }
        return singleInstance;
    }

    /**
     * 上述這段代碼看起來很完美,但是中間存在著一個問題那就是 new DoubleCheckSingleInstance()
     * 這個操作不是原子操作,大致有三步構成。
     * 1. 給instance分配內存
     * 2. 調用DoubleCheckSingleInstance構造函數初始化成員變量
     * 3. 將instance指向內存
     * 問題就出在這一部分,jvm即時編譯器中存在著指令重排序的優化。如果1,2,3的執行步驟不會存在問題
     * 倘若1,3,2
     * 那么當線程二執行完第三步以后,線程三搶占cpu資源,則進行檢查,發現instance不為空,則返回,但此時
     * 成員變量尚未初始化,則會發生調用錯誤。
     * 解決方案是使用volatile來修飾singleInstance來強制每次都從內存映像中讀取數據,其實volitile也可以起到
     * 禁止重排序的功能,在java1.5以后使用比較安全。(1.5之前的內存模型是存在問題的)
     */
}

/**
 * effective java中推薦的內部類實現單例的寫法 。
 */
class SingtonInstance{
    private SingtonInstance()
    {
        System.out.println("constructor");
    }
    private static class SingtonHolder{
        private static SingtonInstance instance = new SingtonInstance();
    }
    public static void beforeInvoke()
    {
        System.out.println("before invoke");
    }

    public static SingtonInstance getInstance()
    {
        /**
         * 此處最開始理解有點繞,但是經過我對象的一個提醒恍然明白,對于內部類的所有對象對于其
         * 外部類來說都是可見的,簡直是霸氣,否則private的可見范圍是不允許直接訪問的。
         */
        return SingtonHolder.instance;
    }
}

public class Test {
    public static void main(String[] args) {
        SingtonInstance.beforeInvoke();
        SingtonInstance.getInstance().beforeInvoke();
    }
}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容