單例模式(學習筆記)

  1. 意圖

   保證一個類只有一個實例, 並提供一個訪問該實例的全局節點java

  2. 動機

  • 控制某些共享資源(數據庫或文件)的訪問權限 
  • 爲該實例提供一個全局訪問點

  3. 適用性

  • 當類只能有一個實例並且客戶能夠從一個衆所周知的訪問點訪問它時
  • 更嚴格的控制全局變量

  4. 結構

    

  5. 效果

  1) 對惟一實例的受控訪問數據庫

  2) 得到指向該實例的全局訪問節點緩存

  3) 僅在首次請求單例對象時進行初始化安全

  4) 違反了單一職責原則。 該模式同時解決了兩個問題(在一個方法中進行了建立類和提供類對象的操做)多線程

  5) 單例模式可能掩蓋不良設計, 好比程序各組件之間相互瞭解過多等app

  6) 該模式在多線程環境下須要進行特殊處理, 避免多個線程屢次建立單例對象框架

  7) 單例的客戶端代碼單元測試可能會比較困難, 由於許多測試框架以基於繼承的方式建立模擬對象。 因爲單例類的構造函數是私有的, 並且絕大部分語言沒法重寫靜態方法, 因此你須要想出仔細考慮模擬單例的方法。 要麼乾脆不編寫測試代碼, 或者不使用單例模式less

  6. 代碼實現

  Singleton_singlethread.java: 單例(單線程)ide

package singleton;

/**
 * @author GaoMing
 * @date 2021/7/18 - 19:13
 */
public class Singleton_singlethread {
    private static Singleton_singlethread instance;
    public String value;

    private Singleton_singlethread(String value) {
        // The following code emulates slow initialization.
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        this.value = value;
    }

    public static Singleton_singlethread getInstance(String value) {
        if (instance == null) {
            instance = new Singleton_singlethread(value);
        }
        return instance;
    }
}

  DemoSingleThread.java: 客戶端代碼函數

package singleton;

/**
 * @author GaoMing
 * @date 2021/7/18 - 19:16
 */
public class DemoSingleThread {
    public static void main(String[] args) {
        System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" +
                "If you see different values, then 2 singletons were created (booo!!)" + "\n\n" +
                "RESULT:" + "\n");
        Singleton_singlethread singleton = Singleton_singlethread.getInstance("FOO");
        Singleton_singlethread anotherSingleton = Singleton_singlethread.getInstance("BAR");
        System.out.println(singleton.value);
        System.out.println(anotherSingleton.value);
    }
}

  執行結果

If you see the same value, then singleton was reused (yay!)
If you see different values, then 2 singletons were created (booo!!)

RESULT:

FOO
FOO

  Singleton_multithread.java: 單例(多線程安全單例)

package singleton;

/**
 * @author GaoMing
 * @date 2021/7/18 - 19:12
 */
public final class Singleton_multithread {
    private static volatile Singleton_multithread instance;

    public String value;

    private Singleton_multithread(String value) {
        this.value = value;
    }

    public static Singleton_multithread getInstance(String value) {
        // The approach taken here is called double-checked locking (DCL). It
        // exists to prevent race condition between multiple threads that may
        // attempt to get singleton instance at the same time, creating separate
        // instances as a result.
        //
        // It may seem that having the `result` variable here is completely
        // pointless. There is, however, a very important caveat when
        // implementing double-checked locking in Java, which is solved by
        // introducing this local variable.
        //
        // You can read more info DCL issues in Java here:
        // https://refactoring.guru/java-dcl-issue
        Singleton_multithread result = instance;
        if (result != null) {
            return result;
        }
        synchronized(Singleton_multithread.class) {
            if (instance == null) {
                instance = new Singleton_multithread(value);
            }
            return instance;
        }
    }
}

  DemoMultiThread.java: 客戶端代碼

package singleton;

/**
 * @author GaoMing
 * @date 2021/7/18 - 19:30
 */
public class DemoMultiThread {
    public static void main(String[] args) {
        System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" +
                "If you see different values, then 2 singletons were created (booo!!)" + "\n\n" +
                "RESULT:" + "\n");
        Thread threadFoo = new Thread(new ThreadFoo());
        Thread threadBar = new Thread(new ThreadBar());
        threadFoo.start();
        threadBar.start();
    }

    static class ThreadFoo implements Runnable {
        @Override
        public void run() {
            Singleton_multithread singleton = Singleton_multithread.getInstance("FOO");
            System.out.println(singleton.value);
        }
    }

    static class ThreadBar implements Runnable {
        @Override
        public void run() {
            Singleton_multithread singleton = Singleton_multithread.getInstance("BAR");
            System.out.println(singleton.value);
        }
    }
}

  執行結果

If you see the same value, then singleton was reused (yay!)
If you see different values, then 2 singletons were created (booo!!)

RESULT:

BAR
BAR

 

  7. 與其餘模式的關係

  •  外觀模式類一般能夠轉換爲單例模式類, 由於在大部分狀況下一個外觀對象就足夠了
  • 抽象工廠模式、 生成器模式和原型模式均可以用單例來實現

  8. 已知應用 

  • java.lang.Runtime#getRuntime()
  • java.awt.Desktop#getDesktop()
  • java.lang.System#getSecurityManager()

  識別方法: 單例能夠經過返回相同緩存對象的靜態構建方法來識別。

相關文章
相關標籤/搜索