我的博客地址https://home.cnblogs.com/u/wdfwolf3/。轉載註明出處,謝謝。面試
面試被要求寫一個單例模式,想不起來雙重鎖的寫法,就寫了一個普通的餓漢模式。簡單問題沒答好,面試減去不少分數。回來翻看筆記,從新過了一遍,在博客中整理記錄一下。安全
1.懶漢式,線程不安全app
public class Lazy { private static Lazy instance; private Lazy(){} public static Lazy getInstance() { if (instance == null) instance = new Lazy(); return instance; } }
2.懶漢式,線程不安全ide
public class Lazy { private static Lazy instance; private Lazy(){} public static synchronized Lazy getInstance() { if (instance == null) instance = new Lazy(); return instance; } }
3.餓漢式,線程安全 函數
public class Hungry { private static Hungry instance = new Hungry(); private Hungry (){} public static Hungry getInstance() { return instance; } }
4.雙重校驗鎖,線程安全優化
因爲線程安全的懶漢式在任什麼時候候只能有一個線程調用 getInstance() 方法,可是同步操做只須要在第一次建立單例實例對象時才被須要,因此它並不高效。這就出現了雙重檢驗鎖來解決這個問題。爲何在同步塊內要檢驗兩次,由於可能會有多個線程一塊兒進入同步塊外的 if,若是在同步塊內不進行二次檢驗的話,阻塞的線程得到鎖以後又會new一個新的對象,就會生成多個實例了。spa
public class DoubleLock { private volatile static DoubleLock instance; private DoubleLock() { } public static DoubleLock getSingleton() { if (instance == null) synchronized (DoubleLock.class) { if (instance == null) instance = new DoubleLock(); } return instance; } }
這裏對instance使用了volatile關鍵字,主要是用它的禁止指令重排序功能。new Singleton()並不是是一個原子操做,在 JVM 中作了下面 3 件事情,線程
a.給 instance 分配內存code
b.調用 Singleton 的構造函數來初始化成員變量orm
c.將instance對象指向分配的內存空間(執行完這步 instance 就爲非 null 了)
JVM可能將語句c優化到語句b以前,當一個線程執行完c沒有執行b,而後釋放鎖,另一個線程得到鎖,此時判斷instance!=null,直接返回,那就會出錯。
5.靜態內部類,線程安全
public class Inner { private static class SingletonHolder { private static final Inner INSTANCE = new Inner(); } private Inner() { } public static final Inner getInstance() { return SingletonHolder.INSTANCE; } }
6.枚舉,線程安全
public enum SingletonEnum { INSTANCE; }
1、懶漢式,線程不安全