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 方法時才去建立這個單例 注意 如果編寫的是多執行緒程式,則不要刪...