設計模式 單例模式的幾種實現方式

2021-08-14 07:52:02 字數 3186 閱讀 5395

1. 概述

單例模式:簡單的說就是可以確保只產生乙個類例項,讓多個使用者或者多個執行緒同時使用這乙個例項,而不需要每次使用都建立一次物件。

2. 優缺點和適用場景

3. 幾種不同形式的單例模式

class singleclass

public

static singleclass getinstance()

}

這種單例的實現方式非常簡單而且可靠,不會涉及到在建立單例時的非執行緒安全問題,因為例項的建立是在類載入時完成的。但是當這種單例不光要承擔建立例項的角色,又要完成其他工作的時候,就有點不那麼得心應手了,比如:

class singleclass

public

static singleclass getinstance()

public

static

void

dosomething()

}

可以看到,這個單例不光要扮演建立例項的角色又要扮演其他角色(dosomething),當我們呼叫singleclass.dosomething()的時候,如果這時類例項還沒建立或者說類還沒載入,虛擬機器在這種場景下就會為我們載入類,並建立例項,然而我們這時並不想讓singleclass產生例項,因為還不需要用到singleclass的例項。那麼有沒有一種方式可以延遲載入單例呢,讓單例的建立能受我們控制,想讓他什麼時候建立就什麼時候建立,而不是類一載入就建立例項。

class lazysingleclass

public

static synchronized lazysingleclass getinstance()

return instance;

}public

static

void

dosomething()

}

當我們呼叫lazysingleclass.dosomething時,儘管虛擬機會載入類,但是不會建立類例項,因為我們把建立類例項的控制權完全交給了getinstance方法,只有我們呼叫getinstance時才會建立例項。雖然解決了延遲載入的問題,但是可以看到getinstance方法是加上了同步鎖的,因為類例項不是在類載入時完成的,所以肯定涉及到非執行緒安全問題,當兩個執行緒呼叫getinstance方法,如果不加上synchronized,乙個執行緒建立完例項前,另乙個執行緒判斷instance是空的,這樣很容易就建立了兩個例項。

getinstance整個方法被加上了同步關鍵字,這樣的效率是很低的,我們可以改良一下,把同步關鍵字就加在涉及執行緒安全問題的**上

class lazysingleclass2

public

static lazysingleclass2 getinstance() }}

return instance;

}public

static

void

dosomething()

}

雙重檢測,已經可以做到執行緒安全了,但要依賴jdk版本,在jdk5.0以後才適用,而且在效率上肯定是比不上餓漢式的。為了解決這個問題,還需要對單例模式進行改進。

class innersingleclass

private

static

class singletonholder

public

static innersingleclass getinstance()

public

static

void

dosomething()

}

當外部類被載入時,內部類不會被初始化,而且將類例項的建立放在內部類載入時完成,避開了非執行緒安全問題。可以看到這種內部類的實現方式,既滿足了延遲載入,又不涉及到非執行緒安全問題。

以上幾種單例模式,的確在大多數情況下能夠確保只產生乙個例項了,但也有例外的情況,當通過反射,強行呼叫單例類的私有建構函式,就會產生多個例項,可以對私有建構函式進行異常檢測。這種反射造成的問題是一種極端的方式,就不過多去討論,還有一種情況就是序列化和反序列化的時候會破壞以上單例。

@test

public

void

test6() throws ioexception, classnotfoundexception

這段程式先將innersingleclass序列化到檔案single.txt,再把它從檔案反序列化為物件,序列化前的物件和反序列化的物件理應是同乙個物件,然而程式輸出為false,事實證明,序列化和反序列化拿到的物件不是同乙個單例,那麼怎麼來避免這一問題發生呢

class innersingleclass implements serializable

private

static

class singletonholder

public

static innersingleclass getinstance()

public

static

void

dosomething()

//新加**

public object readresolve()

}

在單例類中新加方法readresolve就可以了,在反序列化時會自動呼叫readresolve方法。然而還有一種單例模式是支援反序列化的,即不用在單例類裡加上readresolve方法

enum enumanimal

}

這樣就實現了乙個動物類單例,它能保證在反序列化後也是單例的,並且是執行緒安全的,而且也能保證不被反射破壞,具體可以去看反射newinstance()的原始碼,講的很清楚。這種方式是不可以實現延遲載入的。

class user

enum enumsingleclass

public user getinstance()

}

這裡用列舉類enumsingleclass來實現user類的單例。當反序列化user時,發現單例被破壞了,這是毫無疑問的,又不是建立enumsingleclass的單例,而是建立user的單例,要想反序列化後user單例不被破壞,只能在user中新增readresolve方法。

設計模式 單例模式(一)單例的幾種基本實現方式

單例模式餓漢式單例 public class hungrysingleton public static hungrysingleton getinstance 也可以將例項初始化的過程放在靜態 塊中。這兩種寫法都非常的簡單,也非常好理解,餓漢式適用在單例物件較少的情況。懶漢式單例 懶漢式單例的特點...

設計模式 單例模式的幾種建立方式

ensure a class has only one instance,and provide a global point of access to it.確保某乙個類只有乙個例項,而且自行例項化並向整個系統提供這個例項 建立方式分為餓漢式 懶漢式 雙重檢查 靜態內部類 列舉等。靜態屬性建立 採...

單例模式實現的幾種方式

單例模式三個主要特點 1 構造方法私有化 2 例項化的變數引用私有化 3 獲取例項的方法共有。package com.ctl.singleton 懶漢式單例 該模式的特點是類載入時沒有生成單例,只有當第一次呼叫 getlnstance 方法時才去建立這個單例 注意 如果編寫的是多執行緒程式,則不要刪...