所謂單例,就是整個(gè)程序有且僅有一個(gè)實(shí)例。該類負(fù)責(zé)創(chuàng)建自己的對(duì)象,同時(shí)確保只有一個(gè)對(duì)象被創(chuàng)建。在Java,一般常用在工具類的實(shí)現(xiàn)或創(chuàng)建對(duì)象需要消耗資源。
特點(diǎn)
- 類構(gòu)造器私有
- 持有自己類型的屬性
- 對(duì)外提供獲取實(shí)例的靜態(tài)方法
懶漢模式
線程不安全,延遲初始化,嚴(yán)格意義上不是不是單例模式
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
餓漢模式
線程安全,比較常用,但容易產(chǎn)生垃圾,因?yàn)橐婚_始就初始化
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
雙重鎖模式
線程安全,延遲初始化。這種方式采用雙鎖機(jī)制,安全且在多線程情況下能保持高性能。
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
雙重檢查模式,進(jìn)行了兩次的判斷,第一次是為了避免不要的實(shí)例,第二次是為了進(jìn)行同步,避免多線程問題。由于singleton=new Singleton()對(duì)象的創(chuàng)建在JVM中可能會(huì)進(jìn)行重排序,在多線程訪問下存在風(fēng)險(xiǎn),使用volatile修飾signleton實(shí)例變量有效,解決該問題。
靜態(tài)內(nèi)部類單例模式
public class Singleton {
private Singleton(){
}
public static Singleton getInstance(){
return Inner.instance;
}
private static class Inner {
private static final Singleton instance = new Singleton();
}
}
只有第一次調(diào)用getInstance方法時(shí),虛擬機(jī)才加載 Inner 并初始化instance ,只有一個(gè)線程可以獲得對(duì)象的初始化鎖,其他線程無法進(jìn)行初始化,保證對(duì)象的唯一性。目前此方式是所有單例模式中最推薦的模式,但具體還是根據(jù)項(xiàng)目選擇。
枚舉單例模式
public enum Singleton {
INSTANCE;
}
默認(rèn)枚舉實(shí)例的創(chuàng)建是線程安全的,并且在任何情況下都是單例。實(shí)際上
- 枚舉類隱藏了私有的構(gòu)造器。
- 枚舉類的域 是相應(yīng)類型的一個(gè)實(shí)例對(duì)象
那么枚舉類型日常用例是這樣子的:
public enum Singleton {
INSTANCE
//doSomething 該實(shí)例支持的行為
//可以省略此方法,通過Singleton.INSTANCE進(jìn)行操作
public static Singleton get Instance() {
return Singleton.INSTANCE;
}
}
枚舉單例模式在《Effective Java》中推薦的單例模式之一。但枚舉實(shí)例在日常開發(fā)是很少使用的,就是很簡單以導(dǎo)致可讀性較差。
在以上所有的單例模式中,推薦靜態(tài)內(nèi)部類單例模式。主要是非常直觀,即保證線程安全又保證唯一性。
眾所周知,單例模式是創(chuàng)建型模式,都會(huì)新建一個(gè)實(shí)例。那么一個(gè)重要的問題就是反序列化。當(dāng)實(shí)例被寫入到文件到反序列化成實(shí)例時(shí),我們需要重寫readResolve方法,以讓實(shí)例唯一。
private Object readResolve() throws ObjectStreamException{
return singleton;
}