懶漢式(線程不安全)
- 這段代碼比較簡單,使用了懶加載模式,但卻存在致命的問題。當有多個線程並行調用 getInstance() 的時候,就會創建多個實例。也就是說在多線程下不能正常工作。
package singleton;
/**
* 懶漢式
* @author 落幕
*
*/
public class LazySingleTon {
private LazySingleTon() {
}
private static LazySingleTon lazy = null;
public static LazySingleTon getInstance(){
if(lazy == null){
lazy = new LazySingleTon();
}
return lazy;
}
}
- 線程安全的懶漢式,但因爲synchronized鎖的是方法,嚴重影響性能。
/**
* 懶漢式
* @author 落幕
*
*/
public class LazySingleTon {
private LazySingleTon() {
}
private static LazySingleTon lazy = null;
public static synchronized LazySingleTon getInstance(){
if(lazy == null){
lazy = new LazySingleTon();
}
return lazy;
}
}
餓漢式
- 這種方法非常簡單,因爲單例的實例被聲明成 static 和 final 變量了,在第一次加載類到內存中時就會初始化,所以創建實例本身是線程安全的。
package singleton;
/**
* 餓漢式
* @author 落幕
*
*/
public class EagerSingleton {
private EagerSingleton() {
// TODO Auto-generated constructor stub
}
private static final EagerSingleton eager = new EagerSingleton();
public static EagerSingleton getInstance(){
return eager;
}
}
雙重檢測機制(DCL)懶漢式
- 雙重檢測機制,是一種使用同步塊加鎖的方法。程序員稱其爲雙重檢查鎖,因爲會有兩次檢查 instance == null,一次是在同步塊外,一次是在同步塊內。爲什麼在同步塊內還要再檢驗一次?因爲可能會有多個線程一起進入同步塊外的 if,如果在同步塊內不進行二次檢驗的話就會生成多個實例了。
package singleton;
/**
* 雙重檢測機制(DCL)懶漢式
* @author 落幕
*
*/
public class LockSingleton {
private LockSingleton() {
// TODO Auto-generated constructor stub
}
//volatile有內存屏障的功能,可以防止重排序問題,至於重排序這個問題大家可以上網搜搜相關資料
private static volatile LockSingleton lock = null;
public static LockSingleton getInstance(){
if(lock == null){
synchronized(LockSingleton.class){
//判斷是否爲null,提高性能
if(lock == null){
//此處非原子性操作,所以需要在lock上加volatile關鍵字
lock = new LockSingleton();
}
}
}
return lock;
}
}
靜態內部類實現單例
- 推薦使用此方法,這種寫法使用JVM本身機制保證了線程安全問題;初始化靜態數據時,Java提供了的線程安全性保證。除了getInstance() 之外沒有辦法訪問它,因此它是懶漢式的;同時讀取實例的時候不會進行同步,沒有性能缺陷,也不依賴JDK版本。
package singleton;
/**
* 靜態內部類
* @author 落幕
*
*/
public class StaticClass {
private StaticClass() {
// TODO Auto-generated constructor stub
}
private static class ClassLoaders {
private static final StaticClass instance = new StaticClass();
}
public static StaticClass getInstance(){
return ClassLoaders.instance;
}
}
枚舉實現單例
- 也較推薦這種寫,簡單明瞭,並且線程安全,而且還能防止反序列化導致重新創建新的對象
package singleton;
/**
* 枚舉實現單例
* @author 落幕
*
*/
public enum EnumSingleton {
INSTANCE;
}