//餓漢式實現單例模式
public class Hungry {
private Hungry(){}
private static Hungry hungry = new Hungry();
public static Hungry getHungry(){
return hungry;
}
}
優點:實現簡單,類載入的時候就完成了範例化,避免了執行緒的同步問題
缺點:無法實現延遲載入,可能會造成記憶體的浪費(浪費可忽略)
//懶漢式實現單例模式
public class LazySingleton01 {
private LazySingleton01(){}
private static LazySingleton01 lazy ;
public static LazySingleton01 getLazy(){
if(lazy==null){
return new LazySingleton01();
}
return lazy;
}
}
缺點:執行緒不安全,不推薦使用
存線上程安全問題的過程:在執行過程中可能會存在這麼一種情況,有多個執行緒去呼叫getLazy方法來獲取LazySingleton01範例,就有可能發生,第一個執行緒在執行if(lazy = =null)的語句時,還沒執行new LazySingleton01()時,第二個執行緒獲得的lazy也為null,通過了(lazy==null)的判斷,最終兩個執行緒都會new範例。
//懶漢式+同步方法實現單例模式
public class LazySingleton02 {
private LazySingleton02(){}
private static LazySingleton02 lazy;
public static synchronized LazySingleton02 getLazy(){
if(lazy==null){
return new LazySingleton02();
}
return lazy;
}
}
執行緒安全,效率低不推薦使用
對於上述缺陷的改進可能有的人會想到如下的程式碼:
public class LazySingleton03 {
private LazySingleton03(){}
private static LazySingleton03 lazy;
public static LazySingleton03 getLazy(){
if(lazy==null){
synchronized(LazySingleton03.class){
return new LazySingleton03();
}
}
return lazy;
}
}
該寫法一樣是執行緒不安全的,當一個執行緒還沒有範例化LazySingleton03時,另一個執行緒執行到if(lazy==null)這個判斷語句時就會進入if語句,雖然加了鎖,但是等到第一個執行緒執行完lazy=new LazySingleton03()釋放出這個鎖時,另一個進入if語句的執行緒同樣會範例化另外一個LazySingleton03物件。
經過一步步的探索,有了雙重校驗鎖的懶漢式寫法:
//雙重校驗鎖+懶漢式
public class DoubleCheckSingleton {
private DoubleCheckSingleton(){}
private volatile static DoubleCheckSingleton instance;
public static DoubleCheckSingleton getInstance(){
if(instance==null){
synchronized (DoubleCheckSingleton.class){
if(instance==null){
return new DoubleCheckSingleton();
}
}
}
return instance;
}
}
優點:執行緒安全,延遲載入,效率較高
注意此處的volatile:
//雙重校驗鎖+懶漢式
public class DoubleCheckSingleton {
private Socket socket;
private DoubleCheckSingleton(){
this.socket=new Socket();
}
private volatile static DoubleCheckSingleton instance;
public static DoubleCheckSingleton getInstance(){
if(instance==null){
synchronized (DoubleCheckSingleton.class){
if(instance==null){
return new DoubleCheckSingleton();
}
}
}
return instance;
}
}
根據JVM執行時指令重排序和Happens-Before的規則,instance和socket之間的範例化的順序並沒有什麼關係約束,誰在前誰在後都有可能,如果instance最先範例化,那麼socket就不會範例化,呼叫這個方法就會產生空指標異常------>
解決這個方法很簡單,加一個關鍵字 volatite
這個關鍵字能夠保證範例化的順序的相對位置不變,instance範例化永遠在socket之後
//靜態內部類實現單例模式
public class StaticInner {
private StaticInner(){}
private static class inner{
private static StaticInner instance=new StaticInner();
}
public static StaticInner getInstance(){
return inner.instance;
}
}
優點:避免了執行緒不安全,延遲載入,效率高
詳解:這個方法並沒有事先宣告instance的靜態成員,而是把它放到了靜態內部類inner中,inner中定義了靜態變數,並直接進行了範例化,當inner被主動參照的時候就會建立範例,StaticInner在範例建立過程中被收集到()方法中,這個方法是同步方法,
保證了記憶體的可見性,JVM指令的順序執行和原子性。該方法也是單例模式的最好設計之一。
//列舉實現單例模式
public enum EnumSingleton {
INSTANCE;
private EnumSingleton(){}
public void method(){}
}
藉助JDK1.5中新增的列舉來實現單例模式
優點:不僅能避免多執行緒同步問題,而且還能防止反序列化重新建立新的物件,實現非常簡單而且最安全可謂很完美。