【乾貨】無可挑剔的幾種Singleton寫法
阿新 • • 發佈:2019-02-16
餓漢式
public class Singleton1 {
/**
* 宣告final static,保證全域性可訪問且不可變。<br>
* 宣告transient,保證不會被序列化,當然也就不會反序列化生成物件了。
*/
private final static transient Singleton1 instance = new Singleton1();
private Singleton1() {
/**
* 防止通過反射生成物件
*/
if (instance != null ) {
throw new RuntimeException("");
}
}
public static Singleton1 getInstance() {
return instance;
}
}
不管你用不用,只要類初始化了,物件就在那裡。
懶漢式
public class Singleton2 {
/**
* volatile:保證物件生成後,其它執行緒立即可見,避免多餘的加鎖流程。<br>
* transient:保證不會被序列化,當然也就不會反序列化生成物件了。
*/
private static volatile transient Singleton2 instance = null;
private Singleton2() {
/**
* 防止通過反射生成物件
*/
if (instance != null) {
throw new RuntimeException("");
}
}
public static Singleton2 getInstance() {
//用到了雙重檢測和加鎖,目的是最大化的提高程式的執行速度
if (instance == null) {// 檢測物件是否已經生成,此時不加鎖。
synchronized (Singleton2.class) {//加鎖
if (instance == null)// 加鎖情況下檢測物件是否已經生成,防止執行緒在等待鎖時其它執行緒已經生成了物件
instance = new Singleton2();
}
}
return instance;
}
}
只有在使用時才會生成物件,但會導致執行緒加鎖,執行緒競爭嚴重時不建議使用該方式。
靜態內部類
public class Singleton3 {
private static class SingletonHolder {
private final transient static Singleton3 instance = new Singleton3();
}
private Singleton3() {
/**
* 防止通過反射生成物件
*/
if (SingletonHolder.instance != null) {
throw new RuntimeException("");
}
}
public static Singleton3 getInstance() {
return SingletonHolder.instance;
}
}
同時具有懶漢和餓漢兩個模式的優點:避免多執行緒競爭,而且只在使用時才例項化物件。
列舉式
public enum Singleton4 {
INSTANCE;
public void doSomething() {
System.out.println("aha");
}
}
public static void main(String[] args) {
Singleton4.INSTANCE.doSomething();
}
程式碼安全無競爭,寫法簡潔到想哭。
反思
我覺得沒有必要關注太細的細節,比如防止反射,防止序列化;大部分情況下,我們在開發程式碼時都已經預知了單例的使用場景。
大神閆巨集在設計模式一書中提到一種單例模式:建構函式是public的。這樣我們可以在使用單例時呼叫getInstance方法,使用多例時直接new一個就好。因為這個類的使用場景是預先知道的。誰又能說這不是一個很好的單例模式?
不必在意茴香豆有幾種寫法,會寫茴香豆幾個字就好。