單例模式

單例模式

保證一個類僅有一個實例(sInstance), 並提供一個訪問該實例的全局訪問點(getInstance).
這就意味着單例通常有如下兩個特點:

  1. 構造函數是私有的(避免別的地方創建它)
  2. 有一個static的方法來對外提供一個該單例的實例.

優點:

  1. 在內存裏只有一個實例,減少了內存的開銷,尤其是頻繁的創建和銷燬實例(比如管理學院首頁頁面緩存)。
  2. 避免對資源的多重佔用(比如寫文件操作)。

缺點:

  1. 沒有接口,不能繼承

使用場景:

  1. 要求生產唯一序列號。
  2. WEB 中的計數器,不用每次刷新都在數據庫里加一次,用單例先緩存起來。
  3. 創建的一個對象需要消耗的資源過多,比如 I/O 與數據庫的連接等。

1 餓漢式單利

餓漢式單例
顧名思義, 就是很餓, 不管三七二十一先創建了一個實例放着, 而不管最終用不用.

缺點:

  • 在不需要的情況下就隨便生成對象,消耗內存,不可取

2 懶漢式單例

"懶", 也就是現在懶得創建, 等有用戶要用的時候才創建.

3 線程安全的懶漢式單例

利用synchronized關鍵字來修飾對外提供該類唯一實例的接口(getInstance)來確保在一個線程調用該接口時能阻塞(block)另一個線程的調用, 從而達到多線程安全, 避免重複創建單例.

缺點:

  • synchronized有很大的性能開銷. 而且在這裏我們是修飾了getInstance方法, 意味着, 如果getInstance被很多線程頻繁調用時, 每次都會做同步檢查, 會導致程序性能下降.實際上我們要的是單例, 當單例已經存在的時候, 我們是不需要用同步方法來控制的

4 雙重檢查單例模式

這種方式的同步使用會減少同步鎖的佔用比例

缺點

  • 容易出現理解錯誤 不加volatile 關鍵字引發不必要的錯誤

5 靜態內部類單例

這種方式, 通過JVM的類加載方式(虛擬機會保證一個類的初始化在多線程環境中被正確的加鎖、同步), 來保證了多線程併發訪問的正確性.

另外, 由於靜態內部類的加載特性 --- 在使用時才加載, 這種方式也達成了懶加載的目的.

缺點

  • 完美 ,依賴於特定的語言

代碼實例

package com.jack.idea.test.designmode;

/**
 * 單例模式
 *
 * @author ljs.song
 * @date 2017-12-28 15:24
 */
public class Singleton {

    public static void main(String[] args) {
        XiaoLong xiaoLong = new XiaoLong();
        System.out.println(xiaoLong.share("小龍龍布蘭妮四點分享英雄聯盟王者之路和絕地求生吃雞之祕"));


        LeiLei leiLei = new LeiLei();
        System.out.println(leiLei.share("磊磊四點分享英雄聯盟王者之路和絕地求生吃雞之祕"));
    }

}

/**
 * 小龍龍布蘭妮分享
 */
class XiaoLong{

    public Share share(String title){
        Share share = SimpleSingleton.getInstance();
        share.title(title);
        return share;
    }
}

/**
 * 磊磊分享
 */
class LeiLei{
    public Share share(String title){
        Share share = SimpleSingleton.getInstance();
        share.title(title);
        return share;
    }
}

/**
 * 分享整理文件
 */
class Share{
    private String title;

    public void title(String title){
        this.title = title;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Share{");
        sb.append("fileName : " + this.hashCode()).append(" ,");
        sb.append("title='").append(title).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

/**
 * 餓漢式
 */
class SimpleSingleton extends Share{

    private static SimpleSingleton simpleSingleton = new SimpleSingleton();

    private SimpleSingleton(){}

    public static SimpleSingleton getInstance(){
        return simpleSingleton;
    }
}

/**
 * 懶漢式(飽漢)
 */
class LazySingleton extends Share{
    private static LazySingleton lazySingleton;

    private LazySingleton(){}

    public static LazySingleton getInstance(){
        //在有人第一次分享後才創建分享的整理文件
        if(lazySingleton == null){
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}

//線程安全的懶漢單例  synchronized
class SynchronizedLazySingleton extends Share{
    private static SynchronizedLazySingleton synchronizedLazySingleton;

    private SynchronizedLazySingleton(){}

    //粗魯人也,直接鎖定整個方法
    public static synchronized SynchronizedLazySingleton getInstance(){
        //在有人第一次分享後才創建分享的整理文件
        if(synchronizedLazySingleton == null){
            synchronizedLazySingleton = new SynchronizedLazySingleton();
        }
        return synchronizedLazySingleton;
    }
}

/**
 * 雙重檢查鎖定單例
 */
class DCLLazySingleton extends Share{
    /**
     * 這裏添加volatile 關鍵字是因爲下面註釋 問題根源 S處對象的創建
     * 1.分配內存 2.初始化對象 3. 引用指向分配的地址
     * 可能會出現指令重排序
     * 假如 順序變爲 1,3,2 就有可能導致線程A走進S處發生重排序
     * 線程B 在代碼N處發現對象不爲空,但是卻還沒初始化的尷尬
     */
    private static volatile DCLLazySingleton dclLazySingleton;

    private DCLLazySingleton(){}

    public static DCLLazySingleton getInstance(){
        //第一次檢查 N
        if(dclLazySingleton == null){
            synchronized (DCLLazySingleton.class){
                //第二次檢查
                if(dclLazySingleton == null) {
                    // 問題根源 S
                    dclLazySingleton = new DCLLazySingleton();
                }
            }
        }
        return dclLazySingleton;
    }
}

/**
 * 靜態內部類單例
 */
class InnerClassSingleton{

    private InnerClassSingleton(){}

    public static InnerClassSingleton getInstance(){
        return Inner.INNER_CLASS_SINGLETON;
    }

    static class Inner{
        private static final InnerClassSingleton INNER_CLASS_SINGLETON = new InnerClassSingleton();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章