設計模式-單例模式(餓漢式及懶漢式的Java實現)

來源:互聯網
上載者:User

標籤:java   單例模式   設計模式   

單例模式

單例模式在程式設計中使用的頻率非常之高,其設計的目的是為了在程式中提供唯一一個對象(保證只被構造一次),例如寫入日誌的log對象,windows的工作管理員實現(只能開啟一個)。這裡主要介紹單例模式使用Java的實現(包括餓漢式及懶漢式)。

實現

這裡使用Log類作為例子,Log對象需要在程式中只有一個對象且只初始化一次。

餓漢式

餓漢式的單例模式理解起來是比較容易的,就是在單例類載入的時候就初始化需要單例的對象。實現也比較容易。

public class Singleton{    private static Log logObj = new Log();    public static Log getInstance(){        return logObj;    }}
懶漢式

如果logObj需要佔用很大的記憶體,如果一開始就初始化logObj,那麼會佔用大量的記憶體。此時,有人就想,如果我在想用的時候再初始化Log類的對象,像懶漢一樣,只有用到的時候再初始化,需要怎麼設計呢?

實現一(非安全執行緒版本)
public class Singleton{    private static Log logObj = null;    public static Log getInstance(){        if(logObj == null){            logObj = new Log();        }        return logObj;    }}
實現二(安全執行緒版本)
public class Singleton{    private static Log logObj = null;    public static synchronized Log getInstance(){        if(logObj == null){            logObj = new Log();        }        return logObj;    }}

為了實現安全執行緒,這個版本的實現犧牲了一定的效率,如果logObj已經初始化,那麼其他線程還需要同步的進入getInstance方法,會造成效率的損失。於是,有些人實現了下面的版本。

實現三(錯誤版本)
public class Singleton{    private static Log logObj = null;    public static Log getInstance(){        if(logObj == null){            synchronized(Singleton.class){                if(logObj == null){                    logObj = new Log();                }            }        }        return logObj;    }}

乍看起來上面的版本是沒問題的,如果某個線程A發現logObj 還沒初始化,那麼就進入同步塊初始化logObj,如果在這期間有其他線程B進入,那麼線程B就會等待進入同步塊,等待A 線程退出同步塊,logObj 已經初始化了,B 線程進入同步塊後發現logObj 不為null,退出同步塊,不再初始化logObj 。 這樣既實現了安全執行緒,又兼顧了效率,確實是很聰明的編碼方式。但是問題來了,由於指令重排序的存在,會導致Log在完全初始化之前logObj就已經不為null。這樣其他線程可能會得到未完全初始化的對象。

解決方案
  1. JDK1.5版本後擴充了volitile語義,可以保證上述代碼的正確性,為此只要將logObj 聲明為volitile即可(volitile之前只是保證記憶體的可見度而已)。

  2. 使用靜態內部類。

載入一個類時,其內部類不會同時被載入。一個類被載入,若且唯若其某個靜態成員(靜態域、構造器、靜態方法等)被調用時發生。

並且jvm會保證類載入的安全執行緒問題,所以利用這個特性可以寫出兼顧效率與保證安全執行緒的版本。

實現四(兼顧效率與安全執行緒的版本)
public class Singleton{    static class LogHolder{        static Log logObj = new Log();    }    public static Log getInstance(){        return LogHolder.logObj;    }}

這樣在Singleton類載入時,並不會載入LogHolder,也就不會初始化Log,如果有線程訪問getInstance方法,那麼jvm會首先載入LogHolder類,並保證初始化logObj,最後返回logObj

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

設計模式-單例模式(餓漢式及懶漢式的Java實現)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.