1. 程式人生 > >【設計模式】單例模式的八種姿態寫法分析

【設計模式】單例模式的八種姿態寫法分析

目錄

  • 何謂單例模式?
  • 單例模式的優點
  • 單例模式實現整體思路流程
  • 單例模式的適用場景
  • 單例模式的八種姿態寫法
  • JDK原始碼中單例模式的應用
  • 單例模式總結

前言
網上氾濫流傳單例模式的寫法種類,有說7種的,也有說6種的,當然也不排除說5種的,他們說的有錯嗎?其實沒有對與錯,刨根問底,寫法終究是寫法,其本質精髓大體一致!因此完全沒必要去追究寫法的多少,有這個時間還不如跟著宜春去網咖偷耳機、去田裡抓青蛙得了,一天天的....

言歸正傳...單例模式是最常用到的設計模式之一,熟悉設計模式的朋友對單例模式絕對不會陌生。同時單例模式也是比較簡單易理解的一種設計模式。

@

何謂單例模式?

專業術語

單例模式是一種常用的軟體設計模式,其定義是單例物件的類只能允許一個例項存在。許多時候整個系統只需要擁有一個的全域性物件,這樣有利於我們協調系統整體的行為。比如在某個伺服器程式中,該伺服器的配置資訊存放在一個檔案中,這些配置資料由一個單例物件統一讀取,然後服務程序中的其他物件再通過這個單例物件獲取這些配置資訊。這種方式簡化了在複雜環境下的配置管理。

單例模式,簡單的說就是 一個類只能有一個例項,並且在整個專案中都能訪問到這個例項。

單例模式的優點

1、在記憶體中只有一個物件,節省記憶體空間。
2、避免頻繁的建立銷燬物件,可以提高效能。
3、避免對共享資源的多重佔用。
4、可以全域性訪問。

單例模式實現整體思路流程

首先我們要清楚單例模式要求類能夠有返回物件一個引用(永遠是同一個)和一個獲得該例項的方法(必須是靜態方法,通常使用getInstance這個名稱)。

單例模式的常規實現思路大致相同為以下三個步驟:

1、私有構造方法
2、指向自己例項的私有靜態引用
3、以自己例項為返回值的靜態的公有的方法

當然也可以理解為
1、私有化構造方法,讓外部不能new。
2、本類內部建立物件例項【靜態變數目的是為了類載入的時候建立例項】
3、提供一個公有的static靜態方法(一般該方法使用getInstance這個名稱),返回例項物件。

將該類的構造方法定義為私有方法,這樣其他處的程式碼就無法通過呼叫該類的構造方法來例項化該類的物件,只有通過該類提供的靜態方法來得到該類的唯一例項;
在該類內提供一個靜態方法,當我們呼叫這個方法時,如果類持有的引用不為空就返回這個引用,如果類保持的引用為空就建立該類的例項並將例項的引用賦予該類保持的引用。

單例模式的適用場景

由於單例模式有很多獨特的優點,所以是程式設計中用的比較多的一種設計模式。我總結了一下我所知道的適合使用單例模式的場景:

1、需要頻繁例項化然後銷燬的物件。
2、建立物件時耗時過多或者耗資源過多,但又經常用到的物件。
3、有狀態的工具類物件。
4、頻繁訪問資料庫或檔案的物件。

在後面我將會講到JDK中的Runtime類就是使用的餓漢式單例!在Spring MVC框架中的controller 預設是單例模式的!

單例模式的八種姿態寫法

宜春強烈建議:如果是沒有接觸單例模式的讀者朋友強烈建議你們動手敲一遍,不要複製,不然沒效果!

還有一點就是,要真正輕而易舉的理解單例模式,JVM的類載入知識是不能少的,不然你只是會敲的層次,啥?不懂類載入?放心,宜春就是要你會,要你理解透徹。

別翻了,這篇文章絕對讓你深刻理解java類的載入以及ClassLoader原始碼分析【JVM篇二】

其實上面的這篇文章特別重要,上面這篇文章的重要性懂的自然懂,不懂的希望能理解宜春的一片好意,去看一下吧,實在看不懂看不下去在回來看這篇文章就好了,再大不了就把博主一起按在馬桶蓋蓋上....

是不是心裡暖暖的?宜春也不多嗶嗶了,直接擼碼走起....

姿態一:餓漢式1(靜態變數)

package singletonPattern;
//餓漢式(靜態變數)

class Singleton{
    //1、私有化構造方法,讓外部不能new
    private Singleton(){

    }
    //2、本類內部建立物件例項【靜態變數目的是為了類載入的時候建立例項】
    private final static Singleton instance=new Singleton();

    //3、提供一個公有的static靜態方法,返回例項物件
    public static Singleton getInstance(){
        return instance;
    }
}
//以下是測試程式碼=====================

public class SingletenDemo1 {
    public static void main(String[] args) {
        Singleton singleton=Singleton.getInstance();
        Singleton singleton2=Singleton.getInstance();
//驗證一:
        System.out.println(singleton==singleton2);
//驗證二:
        System.out.println(singleton.hashCode());
        System.out.println(singleton2.hashCode());
    }
}

//執行結果:
//        true
//        460141958
//        460141958

/*
餓漢式(靜態變數)方法

優點:寫法簡單,在類載入的時候就完成了例項化,同時也就避免了執行緒同步問題,因此執行緒安全
缺點:由於是在類載入時就完成了例項化,沒有達到懶載入的效果。如果一直沒有使用過這個例項,就造成了記憶體的浪費!

總結:這種方式基於ClassLoader類載入機制避免了多執行緒的同步問題,只不過instance屬性在類載入就例項化,在單例模式中大多數都是呼叫getInstance方法,
     由於getInstance方法是static靜態的,呼叫它肯定會觸發類載入!但是觸發類載入的原因有很多,我們不能保證這個類會通過其他的方式觸發類載入(比如呼叫了其他的static方法)
     這個時候初始化instance就沒有達到lazy loading 懶載入的效果,可能造成記憶體的浪費!

     餓漢式(靜態變數)這種方式可以使用但是會造成記憶體的浪費!

     */

姿態二:餓漢式2(static靜態程式碼塊)

package singletonPattern;
//餓漢式2(static靜態程式碼塊)

class Singleton2{
    private Singleton2(){

    }

    private static Singleton2 instance;

    static{ //把建立單例物件的操作放進了static靜態程式碼塊中==============
        instance = new Singleton2();
    }

    public static Singleton2 getInstance(){
        return instance;
    }
}
//餓漢式2(static靜態程式碼塊)其實和第一種餓漢式(靜態變數)方法差不多,其優缺點一致!
//唯一不同的就是把建立單例物件的操作放進了static靜態程式碼塊中

姿態三:懶漢式1(執行緒不安全)

package singletonPattern;
//懶漢式1(執行緒不安全)
class Singleton3{
    private Singleton3(){

    }

    private static Singleton3 instance;

    public static Singleton3 getInstance(){
        if(instance == null){
            instance=new Singleton3();
        }
        return instance;
    }
}
/*
懶漢式(執行緒不安全)的這種方式起到了懶載入的效果,但只能在單執行緒下使用。
如果在多執行緒下,一個執行緒進入了if(singleton==null)判斷語句塊,還沒執行產生例項的句子,另一個執行緒
又進來了,這時會產生多個例項,所以不安全。

結語:懶漢式(執行緒不安全)在實際開發中,不要使用這種方式!!存在潛在危險
*/

姿態四:懶漢式2(執行緒安全)

package singletonPattern;
//懶漢式2(執行緒安全)
class Singleton4{
    private Singleton4(){

    }

    private static Singleton4 instance;

    public static synchronized Singleton4 getInstance(){
        if(instance == null){
            instance=new Singleton4();
        }
        return instance;
    }
}

/*
懶漢式2(執行緒安全)方式

優點:執行緒安全
缺點:效率太低,每次呼叫getInstance方法都要進行同步

結語:懶漢式2(執行緒安全)方式在開發中不推薦使用,主要是效率太低了*/

姿態五:餓漢式2(static靜態程式碼塊)

package singletonPattern;
//懶漢式3 同步程式碼塊(執行緒安全) 但是不滿足單例,在多執行緒下依舊會有多個例項
class Singleton5{
    private Singleton5(){

    }

    private static Singleton5 instance;

    public static  Singleton5 getInstance(){
        if(instance == null){   //多執行緒情況下可能多個執行緒進入這個if塊
            synchronized (Singleton5.class){  //到這裡只會一個一個建立例項,雖然安全,但是就不再是單例了
                instance=new Singleton5();
            }
        }
        return instance;
    }
}
/*
懶漢式3 同步程式碼塊(執行緒安全) 但是不滿足單例,依舊會有多個例項

結語:懶漢式3 同步程式碼塊(執行緒安全)方式在開發中不使用 ,實際上這個單例設計的有點搞笑*/

姿態六:雙重檢查單例

package singletonPattern;
//雙重檢查應用例項方式
class Singleton6{
    private Singleton6(){}

    private static volatile Singleton6 singleton;

    public static Singleton6 getInstance(){
        if(singleton==null){
            synchronized(Singleton6.class){
                if(singleton == null){
                    singleton= new Singleton6();
                }
            }
        }
        return singleton;
    }
}
/*
雙重檢查應用例項方式:

執行緒安全、延遲載入、效率較高

結語:開發中推薦使用!
*/

這個時候博主就得嗶嗶幾句了,細心的童鞋會發現有一個Volatile關鍵字,完了,沒見過,小白童鞋慌了!

Volatile 變數具有 synchronized 的可見性特性,但是不具備原子特性。這就是說執行緒能夠自動發現 volatile 變數的最新值。

這種實現方式既可以實現執行緒安全地建立例項,而又不會對效能造成太大的影響。它只是第一次建立例項的時候同步,以後就不需要同步了,從而加快了執行速度。

姿態七:靜態內部類單例

package singletonPattern;
//static靜態內部類單例

class Singleton7{
    private Singleton7(){}

    private static volatile Singleton7 instance;

    //寫一個static靜態內部類,給該類新增一個static靜態instance屬性
    private static class SingletonInstance{
        private static final Singleton7 SINGLETON_7=new Singleton7();
    }

    //
    public static synchronized Singleton7 getInstence(){
        return SingletonInstance.SINGLETON_7;
    }
}
/*
靜態內部類單例方式
        1、這種方式採用了類載入機制來保證初始化例項時只有一個執行緒
        2、巧妙的將例項化Singleton操作放進getInstance方法中,getInstance方法返回靜態內部類中例項化好的Singleton
        3、類的靜態屬性只會在第一次載入類的時候初始化,也就是隻會初始化一次,在這裡,JVM幫我們保證了執行緒的安全,類在初始化時,別的執行緒無法進入。
       
        優點:執行緒安全、利用靜態內部類特點實現延遲載入、效率高
        開發中推薦使用這種靜態內部類單例方式!

static靜態內部特點:
1、外部類載入不會導致內部類載入,保證了其懶載入
*/

這個單例,宜春就不得不嗶嗶兩句了,要清楚這個單例,必須要明白static靜態內部特點,也就是外部類載入不會導致內部類載入!

姿態八:餓漢式2(static靜態程式碼塊)

package singletonPattern;
//使用列舉

import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader;

enum Singleton8{
    INSTANCE;
    public void methodName(){
        System.out.println("測試資料");
    }
}
/*

列舉方式的列舉:
推薦寫法,簡單高效。充分利用列舉類的特性,只定義了一個例項,且列舉類是天然支援多執行緒的。
藉助JDK1.5中新增的列舉來實現單例模式優點:
         1、不僅能避免多執行緒同步問題 
         2、還能防止反序列化重新建立新的物件

列舉方式單例是由Effective java作者Josh Bloch提倡的,結語:推薦使用!
*/

當然也可以測試一下

public class SingletonDemo8 {
    public static void main(String[] args) {
        Singleton8 instance = Singleton8.INSTANCE;
        Singleton8 instance2 = Singleton8.INSTANCE;
        System.out.println(instance==instance2);

        System.out.println(instance.hashCode());
        System.out.println(instance2.hashCode());

        instance.methodName();
    }
}

執行結果:

true
460141958
460141958
測試資料

屬實沒毛病!

JDK原始碼中單例模式的應用

先來看一段Runtime 的原始碼吧,並分析一下其使用的是種單例模式!

/**
 * Every Java application has a single instance of class
 * <code>Runtime</code> that allows the application to interface with
 * the environment in which the application is running. The current
 * runtime can be obtained from the <code>getRuntime</code> method.
 * <p>
 * An application cannot create its own instance of this class.
 *
 * @author  unascribed
 * @see     java.lang.Runtime#getRuntime()
 * @since   JDK1.0
 */
public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}

這應該不難看出吧!如果看不出的話只能說明你真的還沒有理解單例模式,我其實想說單例模式其實是23種設計模式中最簡單的一個,只是寫法比較多而已!同時面試官一般都會問單例模式,它已經是很基礎的了,問的稍微有點水平就是問你單例模式在JDK中哪裡運用到了,顯然JDK中的Runtime其實它使用的就是餓漢式單例!正如註釋所說,每一個java應用程式都有一個Runtime例項。Runtime的單例模式是採用餓漢模式建立的,意思是當你載入這個類檔案時,這個例項就已經存在了。

Runtime類可以取得JVM系統資訊,或者使用gc()方法釋放掉垃圾空間,還可以使用此類執行本機的程式。

==還有就是spring Mvc 中的controller 預設是單例模式的,解析。==

單例模式總結

1、餓漢式(靜態變數)這種方式可以使用,但是沒有達到 lazy loading 懶載入的效果會造成記憶體的浪費!開發中不建議使用。
2、餓漢式(static靜態程式碼塊)其實和第一種餓漢式(靜態變數)方法差不多,其優缺點一致!唯一不同的就是把建立單例物件的操作放進了static靜態程式碼塊中
3、懶漢式(執行緒不安全)起到了懶載入的效果,但只能在單執行緒下使用。在實際開發中,不要使用這種方式!!!
4、懶漢式2(執行緒安全)方式執行緒安全但是效率太低,每次呼叫getInstance方法都要進行同步。所以在開發中不推薦使用。 5、懶漢式3
同步程式碼塊(執行緒安全)方式在開發中不使用 ,實際上這個設計有點搞笑哈哈。
6、雙重檢查應用例項方式,執行緒安全、延遲載入、效率較高。因此開發中推薦使用!
7、靜態內部類單例方式執行緒安全、利用靜態內部類特點實現延遲載入、效率高。 開發中推薦使用這種靜態內部類單例方式!
8、藉助JDK1.5中新增的列舉來實現單例模式不僅能避免多執行緒同步問題還能防止反序列化重新建立新的物件。列舉方式單例是由Effective java作者Josh Bloch提倡的,開發中推薦使用!

單例模式必須考慮到在多執行緒的應用場合下的使用,畢竟現在的伺服器基本上都是多核的了。

如果本文對你有一點點幫助,那麼請點個讚唄,謝謝~

最後,若有不足或者不正之處,歡迎指正批評,感激不盡!如果有疑問歡迎留言,絕對第一時間回覆!

歡迎各位關注我的公眾號,一起探討技術,嚮往技術,追求技術,說好了來了就是盆友喔...

相關推薦

設計模式模式姿態寫法分析

目錄 何謂單例模式? 單例模式的優點 單例模式實現整體思路流程 單例模式的適用場景 單例模式的八種姿態寫法 JDK原始碼中單例模式的應用 單例模式總結

設計模式模式

amp 就會 保持 占用 back obj 線程不安全 only 大量 一、單例模式的定義 單例模式( Singleton Pattern) 是一個比較簡單的模式, 其定義如下:Ensure a class has only one instance, and provid

設計模式模式的理解與場景舉例

數據 好處 ask 配置管理 關鍵詞 簡潔 多次 nag 序列 軟件設計常用的一種設計模式 —— 單例模式 體現:在應用這個模式時,單例對象的類必須保證只有一個實例存在。 好處:許多時候整個系統只需要擁有一個全局的對象,這樣有利於我們協調整個系統的行為。 場景舉例 1.服務

設計模式模式 |建造者模式 |原型模式

單例模式 通常我們讓一個全域性變數使得一個物件被訪問,但它不能防止你例項化多個物件。一個最好的辦法就是,讓類自身負責八寸他的唯一例項。這個類可以保證沒有其他例項可以被建立,並且它可以提供一個訪問該例項的方法。 單例模式(Singletion):保證

設計模式模式(Singleton)

思想 保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。 類圖 Singleton 類稱為單例類,該類的建構函式是 Private 的,這是為了禁止從 Singleton 類的外部呼叫建構函式,這就堵死了外界利用 new 建立此類的可能。通過 getInstance 方

JS設計模式模式

單例模式就如其名一樣,一個建構函式在構造出一個物件後,之後再使用這個建構函式構造物件時,不會是新的物件,依舊是上一次的物件,通俗點說,單例模式實現的是一個建構函式只能創造一個新的物件。很不解,為什麼要這麼做?這個就像什麼呢,舉個不恰當的例子,就像一家汽車製造廠,在製造完一輛車

設計模式模式、三個工廠模式

interface IFactory { Operation CreateOperation(); } class AddFactory : IFactory { public Operation CreateOperation()

設計模式 模式的幾寫法

          單例模式是一種物件建立型模式,使用單例模式,可以保證為一個類只生成唯一的例項物件。也就是說,在整個程式空間中,該類只存在一個例項物件。     其實,GoF對單例模式的定義是:保證

設計模式——模式的幾寫法

單例模式:保證一個類僅有一個例項,並提供一個訪問他的全域性點。 懶漢式,執行緒不安全 //懶漢式,執行緒不安全 public class Sington { private Sington(){};//讓外界不能通過new來建立例項 private

設計模式模式的絕對及執行緒安全

1. 餓漢模式 /** * 餓漢模式 * @author 鄭海鵬 * @since 2015年7月6日 */ public class Singleton { private st

設計模式模式之執行緒類

好記性,不如爛筆頭。對於單例模式的理解和應用還是需要多多實踐,這次有感而發,寫份基於執行緒執行的單例模式。 單例模式該怎樣去實現:建構函式宣告為private或protect防止被外部函式例項化,內部

Java設計模式模式

package com.bufoon.test.gof.singleton; /** * 顧名思義,比較懈怠,就是需要的時候才會去建立例項, * 這是經典的延遲載入思想,還有快取的實現思路; * 它會有執行緒安全問題,可以加鎖,但是會影響效率, * 典型的時間換空間的策略; * @ClassName

7.13模式(Singleton)的用法和用處以及破解

vol 創建者模式 code private let .get span test loaded 1):用處   是一種創建者模式,只生成一個實例對象,具有全局唯一性,當一個對象的產生需要比較多的資源時, 如讀取配置(如數據庫連接池、Spring中, 一個Compone

Java學習模式

{} on() new 情況下 餓漢模式 syn 效率 存在 情況 單例模式有兩種:餓漢模式和懶漢模式,懶漢模式的特點是延遲加載實例 //餓漢模式class Singleton1{ private static final Singleton1 instance = ne

C/C++模式 Singleton

Backto C/C++ Index //.h class Singleton { public: static Singleton* instance(); static void Close(); private: Singleton(); ~Singleton

C#設計模式之一模式(Singleton Pattern)創建型

nal 設計 類庫 開發 避免 sum behavior 並且 負責 原文:C#設計模式之一單例模式(Singleton Pattern)【創建型】一、引言 看了李建忠老師的講的設計模式已經有一段時間了(這段時間大概有一年多了),自己還沒有寫過自己的、有關設計模

javascriptjavascript設計模式模式

函數 設置 tin 私有 資源 公有 key fun 初始化 單例模式: 定義:單例模式之所以這麽叫,是因為它限制一個類只能有一個實例化對象。 實現方法:判斷實例是否存在,如果存在則直接返回,如果不存在就創建了再返回。(確保一個類只有一個實例對象) 特點: 命名空間提供者,

設計模式模式內附物件例項化幾方式、實現執行緒安全幾方式

繼續來複習常用的設計模式-單例模式,順便回憶一下執行緒安全的幾種實現方式。 一、什麼是單例模式 單例模式,簡單常用的一種設計模式,也很好的體現了程式碼控制物件在記憶體數量的一種方式,主要分2種實現方式: ①餓漢式,執行緒安全 ②懶漢式,執行緒不安全(新增鎖機制,可以實現執行緒安全)

設計模式-2模式

優點: 記憶體在系統執行過程中只有一個例項,減少記憶體開銷。 允許可變數目的例項 其他類通過唯一的全域性例項,可以方便訪問單例中的方法和變數。 單例可在第一次使用時候,進行例項化,不必在系統剛啟動就初始化。一定程度上可以控制自己例項化程序。 缺點: 過多

JAVA設計模式模式

懶漢 常見 單例 str 自己 餓漢式 span color 實例 前言 java最常見的設計模式就是單例模式,而單例模式最常見的就是懶漢式和餓漢式,現在就分別看一下 1.懶漢式 懶漢式比較懶,當別人用到時才會初始化實例,而當有多個人同時用到就可能產生多個實例,造成線程