設計模式之單例


一、什么是單例模式:

  所謂類的單例設計模式,就是采取一定的方法保證在整個的軟件系統中,對某個類只能存在一個對象實例,並且該類只提供一個取得其對象實例的方法(靜態方法)。

二、單例模式的實現方式:(7種)

  1.餓漢式(靜態變量):

//餓漢式(靜態變量)
class Singleton {
    
    //1. 構造器私有化, 外部能new
    private Singleton() { } //2.本類內部創建對象實例 private final static Singleton instance = new Singleton(); //3. 提供一個公有的靜態方法,返回實例對象 public static Singleton getInstance() { return instance; } }

    優:寫法比較簡單,就是在類裝載的時候就完成實例化。避免了線程同步問題。 

    缺:在類裝載的時候就完成實例化,沒有達到Lazy Loading的效果。如果從始至終從未使用過這個實例,則會造成內存的浪費。

    評價:這種模式可用,但可能會造成內存浪費

  2.餓漢式(靜態代碼塊):

class Singleton {
    
    //1. 構造器私有化, 外部能new
    private Singleton() { } //2.本類內部創建對象實例 private static Singleton instance; static { // 在靜態代碼塊中,創建單例對象 instance = new Singleton(); } //3. 提供一個公有的靜態方法,返回實例對象 public static Singleton getInstance() { return instance; } }

    與上面類似

  3.懶漢式(線程不安全): 

class Singleton {
    private static Singleton instance; private Singleton() {} //提供一個靜態的公有方法,當使用到該方法時,才去創建 instance //即懶漢式 public static Singleton getInstance() { if(instance == null) { instance = new Singleton(); } return instance; } }

    優:起到了Lazy Loading的效果 

    缺:多線程環境下不可使用這種方式

    評價:只能在單線程下使用

  4.懶漢式(線程安全,同步方法)

// 懶漢式(線程安全,同步方法)
class Singleton {
    private static Singleton instance; private Singleton() {} //提供一個靜態的公有方法,加入同步處理的代碼,解決線程安全問題 //即懶漢式 public static synchronized Singleton getInstance() { if(instance == null) { instance = new Singleton(); } return instance; } }

    優:解決了線程不安全問題

    缺:效率太低了,每個線程在想獲得類的實例時候,執行getInstance()方法都要進行 同步

    評價:不推薦使用

  5.雙重檢查

class Singleton {
    private static volatile Singleton instance; private Singleton() {} //提供一個靜態的公有方法,加入雙重檢查代碼,解決線程安全問題, 同時解決懶加載問題 //同時保證了效率, 推薦使用 public static synchronized Singleton getInstance() { if(instance == null) { synchronized (Singleton.class) { if(instance == null) { instance = new Singleton(); } } } return instance; } }

    評價:線程安全;延遲加載;效率較高 ,推薦使用

  6.靜態內部類:   

// 靜態內部類完成, 推薦使用
class Singleton {
    private static volatile Singleton instance; //構造器私有化 private Singleton() {} //寫一個靜態內部類,該類中有一個靜態屬性 Singleton private static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } //提供一個靜態的公有方法,直接返回SingletonInstance.INSTANCE public static synchronized Singleton getInstance() { return SingletonInstance.INSTANCE; } }

    評價:

  • 靜態內部類方式在外部類被裝載時並不會立即實例化,而是在需要實例化 時,才會裝載內部類,從而完成內部的 實例化。
  • 類的靜態屬性只會在第一次加載類的時候初始化,所以在這里,JVM幫助我們保證了線程的安全性,在類進行初始化時,別的線程是無法進入的。
  • 避免了線程不安全,利用靜態內部類特點實現延遲加載,效率高,推薦使用

  7.枚舉類

public class SingletonTest08 {
    public static void main(String[] args) { Singleton instance = Singleton.INSTANCE; Singleton instance2 = Singleton.INSTANCE; System.out.println(instance == instance2); } } //使用枚舉,可以實現單例, 推薦 enum Singleton { INSTANCE; //屬性 public void sayOK() { System.out.println("ok~"); } }

    評價:借助JDK1.5中添加的枚舉來實現單例模式。不僅能避免多線程同步問題,而 且還能防止反序列化重新創建新的對象,推薦使用

  8.線程池

 

public class Singleton {
    
    private Singleton() {} private static final ThreadLocal<Singleton> tlSingleton = new ThreadLocal<Singleton>() { @Override protected Singleton initialValue() { return new Singleton(); } }; public static Singleton getInstance() { return tlSingleton.get(); } } 

  9.CAS

public class Singleton {

    /**利用AtomicReference */
    private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>(); private Singleton(){} //用CAS確保線程安全 public static final Singleton getInstance(){ for (;;) { Singleton current = INSTANCE.get(); if (null != current) { return current; } current = new Singleton(); if (INSTANCE.compareAndSet(null, current)) { return current; } } } public static void main(String[] args) { Singleton singleton1 = Singleton.getInstance(); Singleton singleton2 = Singleton.getInstance(); System.out.println(singleton1 == singleton2); } } 

 

 

三、單例模式的jdk使用:  

package java.lang;
 
import java.io.*; import java.util.StringTokenizer; public class Runtime {//這里使用了餓漢式,因為runtime肯定會用到,不會浪費資源 private static Runtime currentRuntime = new Runtime(); public static Runtime getRuntime() { return currentRuntime; } /** Don't let anyone else instantiate this class */ private Runtime() {}

 

四、一些常見問題:https://blog.csdn.net/qq_40868987/article/details/103266261

  1.靜態類和單例的區別:

單例可以繼承和被繼承,方法可以被override,而靜態方法不可以。

靜態方法中產生的對象會在執行后被釋放,進而被GC清理,不會一直存在於內存中。

靜態類會在第一次運行時初始化,單例模式可以有其他的選擇,即可以延遲加載。

靜態方法如果反復的初始化和釋放,則會占用很多資源,而使用單例模式將其常駐於內存可以更加節約資源。

靜態方法有更高的訪問效率。

單例模式很容易被測試。

  2.序列化和反序列化會破壞單例嗎?

     會,解決方案:

  (1)https://blog.csdn.net/weixin_34087301/article/details/91400619

  (2)https://www.cnblogs.com/chiclee/p/9097772.html

  3.線程池的單例實現:

  https://www.cnblogs.com/zhuangyan728/p/6706746.html

  https://mp.weixin.qq.com/s?src=11&timestamp=1586451635&ver=2268&signature=UhZ9OtKUNP-KVVt8s2q8t0MlH4P5Rts5d0hDebj9u0IWlovSgkG0bM8AQ-oLvRFXjkj8CdeTHSLLyedvrK5aMOfYd*U2mzXxINoDzykwmsI-jC1xJ5gNx*SLFWOJz0ys&new=1


免責聲明!

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



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