程序員築基之路——如何寫好一個單例

如何寫好一個單例

[TOC]java

什麼是單例模式?

  • 若是你據說過設計模式,那麼確定知道單例模式,由於單例模式是設計模式中最簡單的一種。顧名思義:單例模式就是一個類只有一個實例變量的一種設計模式,經過使用單例模式,能夠節約系統的資源開銷,避免共享資源的多重佔用等優勢。
  • 何時會用:
    1. 對於那種常常實例化可是過一下子就被銷燬的對象適合使用單例模式。
    2. 對於建立對象須要消耗不少資源的對象。如:數據庫鏈接池對象,線程池對象等
    3. 只須要一個對象保證全局的一致性的。如:Android中Application對象,網站的計數器等。

實現一個單例模式

  • 若是你是一位對設計模式略有接觸的新手,必定會絕不費力的就寫出瞭如下單例代碼(懶漢式單例:等到須要時再實例化):面試

    /** * Created by forever on 2017/9/20. */
    public class Singleton {
        private static Singleton singleton;
    
        private Singleton() {
    
        }
    
        public static Singleton getInstance() {
            if (singleton == null) {
                singleton = new Singleton();
            }
            return singleton;
        }
    }
    複製代碼

    其實上面的代碼就已經涵蓋了單例模式最重要的三個要素:數據庫

    1. 將構造方法私有化(保證外部不能直接構造)。
    2. 有一個靜態屬性指向實例
    3. 提供一個公有的靜態方法向外面提供這個實例。
  • 然而表面看似完美的代碼,內部其實暗藏殺雞:設計模式

    這裏寫圖片描述

    在單線程中看似是沒有什麼問題的,但若是放在多線程的環境中就會有問題了。加入有兩個線程同時訪問getInstance方法,若是期中一個線程剛進入if (singleton == null){}裏面,這個時候另外一個線程剛好也訪問這個方法,而且完成建立了一個實例,那個剛剛掛起的那個線程繼續運行的話就會再建立一個實例。那咱們單例的理想不就破滅的了嘛。多線程

    這裏寫圖片描述

基本方法的改進

  • 既然瞭解了問題,那麼咱們如何才能防止兩個線程同時實例化方法呢?有經驗的同窗或許就會馬上想到了Java的同步。經過synchronized關鍵字進行加鎖。性能

    public synchronized Singleton getInstance() {
            if (singleton == null) {
                singleton = new Singleton();
            }
            return singleton;
        }
    複製代碼
  • 不過加鎖的話對程序的性能性能會有很大影響,若是當某個線程正在訪問該方法的時候其餘線程就只能在鎖池中等待該線程釋放鎖,咱們稍加改進一下:網站

    public Singleton getInstance() {
            if (singleton == null) {
                synchronized (Singleton.class){
                    if(singleton == null){
                        singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }
    複製代碼

    這樣只在構造實例代碼的時候加鎖,對程序的性能影響就小多了。並且只要實例化完成以後,後面基本就不會進入這個同步代碼塊了。spa

  • 看似已經很完美了,那麼咱們有什麼其餘辦法不用加鎖的方式也能避免多線程的問題呢?ok,固然有的,咱們可使用餓漢式的單例:線程

    /** * Created by forever on 2017/9/20. */
    public class Singleton {
        public Singleton singleton = new Singleton();
    
        private Singleton() {
        }
    
        public Singleton getInstance() {
            return singleton;
        }
    }
    複製代碼

    上面代碼與懶漢式加載最大的區別在於這裏的single在開始就實例化了,也就是無論咱們是否使用它,都會將其加載到內存中去。這個在獲取的時候就直接返回就好了。若是不在乎內存的話最好使用這個方法。設計

  • 若是你說,我既不想要使用同步,但又十分在乎內存資源怎麼辦,ok,說明你是一個頗有追求的人,其實在也是有辦法的(辦法總比問題多):

    public class Singleton {
    
        private Singleton(){
    
        }
    
        public static Singleton getInstance(){
            return Nested.singleton;
        }
    
        public static class Nested{
            static {
                System.out.println("蛤蛤");
            }
            private static Singleton singleton = new Singleton();
        }
    }
    複製代碼

    這個時候咱們就須要一個內部類做爲橋樑了,當咱們getInstance()時,類加載器纔會去加載Nested,而後實例化Singleton的實例,若是你對Java的類加載機制有了解的話必定很容易就理解了上述代碼。

總結

  • Java的單例模式看似簡單,其實深究而言仍是有不少值得思考的東西的,若是在面試的時候碰到了也能夠和麪試官多吹逼一下子。最近一直在準備校招並且還要完成學校的實習,做爲菜鳥表示真的很忙,也祝願你們都能找到滿意的工做。若是發現寫的有什麼問題歡迎指正,但願與你們共同進步。
相關文章
相關標籤/搜索