Singleton Pattern

Sometimes it's important to have only one instance for a class. For example, in a system there should be only one window manager (or only a file system or only a print spooler). Usually singletons are used for centralized management of internal or external resources and they provide a global point of access to themselves.

Intent

  • Ensure that only one instance of a class is created.
  • Provide a global point of access to the object.

Implementation

The implementation involves a static member in the "Singleton" class, a private constructor and a static public method that returns a reference to the static member.

Instead of marking the getInstance() as synchronized, we use “double-checked locking” to reduce the use of synchronization in getInstance().

public class Singleton {
    private volatile static Singleton uniqueInstance;
    // The volatile keyword makes sure multi threads handle the
    // uniqueInstance variable correctly

    private Singleton();

    public static Singleton getInstance() {
        if (uniqueInstance == null) {
        // Check for an instance and if there isn't one, enter a
        // synchronized block
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    // Once in the block, check again and if still null,
                    // create an instance
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

Note: Double-checked locking doesn’t work in Java 1.4 or earlier!

Double-Checked Locking is Broken

Background

Double-Checked Locking is widely cited and used as an efficient method for implementing lazy initialization in a multithreaded environment.

However, the following snippet won't work in the presence of either optimizing compilers or shared memory multiprocessors.

// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo {
    private Helper helper = null;
    public Helper getHelper() {
        if (helper == null)
            synchronized(this) {
            if (helper == null)
            helper = new Helper();
        }
      return helper;
  }
  // other functions and members...
}

The most obvious reason it doesn't work it that the writes that initialize the Helper object and the write to the helper field can be done or perceived out of order. Thus, a thread which invokes getHelper() could see a non-null reference to a helper object, but see the default values for fields of the helper object, rather than the values set in the constructor.

There are some other reasons blocking the usages of the Double-Checked Locking. As stated in the The "Double-Checked Locking is Broken" Declaration:

There are lots of reasons it doesn't work. The first couple of reasons we'll describe are more obvious. After understanding those, you may be tempted to try to devise a way to "fix" the double-checked locking idiom. Your fixes will not work: there are more subtle reasons why your fix won't work. Understand those reasons, come up with a better fix, and it still won't work, because there are even more subtle reasons.

Lots of very smart people have spent lots of time looking at this. There is no way to make it work without requiring each thread that accesses the helper object to perform synchronization.

volatile

As of J2SE 5.0, this problem has been fixed. The volatile keyword now ensures that multiple threads handle the singleton instance correctly. The new Java memory model and volatile keyword ensures a happen-before relationship, which requires any write before the read to the volatile variable has to really happen before the read. In a nutshell, once the helper is declared as volatile, we won't read the Helper object between it is initialized and the write to its fields. Its constructor is guaranteed to be called before we read the helper's value.

Don't forget the volatile keyword!!

Initialization-on-demand holder idiom

In software engineering, the Initialization on Demand Holder (design pattern) idiom is a lazy-loaded singleton. In all versions of Java, the idiom enables a safe, highly concurrent lazy initialization with good performance.

public class Something {
    private Something() {}

    private static class LazyHolder {
        private static final Something INSTANCE = new Something();
    }

    public static Something getInstance() {
        return LazyHolder.INSTANCE;
    }
}

From the wikipedia on this topic:

Since the class initialization phase is guaranteed by the JLS to be serial, i.e., non-concurrent, no further synchronization is required in the static getInstance method during loading and initialization. And since the initialization phase writes the static variable INSTANCE in a serial operation, all subsequent concurrent invocations of the getInstance will return the same correctly initialized INSTANCE without incurring any additional synchronization overhead

The semantics of Java guarantee that the field will not be initialized until the field is referenced, and that any thread which accesses the field will see all of the writes resulting from initializing that field.

Singleton property with private constructor

All calls to Elvis.getInstance() below return the same object reference and other Elvis instance will be created.

public class Elvis {
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() {...}

    public static Elvis getInstance() {
        return INSTANCE;
    }
}

An enum type with one element

This approach is functionally equivalent to setting a public static final field, except that it is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks.

public enum Elvis {
    INSTANCE;

    public void someMethod() {...}
}

As pointed out by Effective Java, this is the best way to implement a singleton.

Resource

Hackjustu Dojo (my blog)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末方妖,一起剝皮案震驚了整個(gè)濱河市享完,隨后出現(xiàn)的幾起案子扭屁,更是在濱河造成了極大的恐慌萍聊,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡蹦骑,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門臀防,熙熙樓的掌柜王于貴愁眉苦臉地迎上來眠菇,“玉大人,你說我怎么就攤上這事袱衷∩臃希” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵致燥,是天一觀的道長登疗。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么辐益? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任断傲,我火速辦了婚禮,結(jié)果婚禮上智政,老公的妹妹穿的比我還像新娘认罩。我一直安慰自己,他們只是感情好续捂,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布垦垂。 她就那樣靜靜地躺著,像睡著了一般牙瓢。 火紅的嫁衣襯著肌膚如雪劫拗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天矾克,我揣著相機(jī)與錄音页慷,去河邊找鬼。 笑死聂渊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的四瘫。 我是一名探鬼主播汉嗽,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼找蜜!你這毒婦竟也來了饼暑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤洗做,失蹤者是張志新(化名)和其女友劉穎弓叛,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诚纸,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡撰筷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了畦徘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片毕籽。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖井辆,靈堂內(nèi)的尸體忽然破棺而出关筒,到底是詐尸還是另有隱情,我是刑警寧澤杯缺,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布蒸播,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏袍榆。R本人自食惡果不足惜胀屿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蜡塌。 院中可真熱鬧碉纳,春花似錦、人聲如沸馏艾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽琅摩。三九已至铁孵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間房资,已是汗流浹背蜕劝。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留轰异,地道東北人岖沛。 一個(gè)月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像搭独,于是被迫代替她去往敵國和親婴削。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容