對象發佈:就是提供一個對象的引用給做用域以外的代碼。好比return一個對象,或者做爲參數傳遞到其餘類的方法中。java
對象逸出:一種錯誤的發佈,當一個對象尚未構造結束就已經提供給了外部代碼一個對象引用即發佈了該對象,此時叫作對象逸出,對象的逸出會破壞線程的安全性。數組
咱們最須要關注的就是對象逸出的問題,在不應發佈該對象的地方就不要發佈該對象,例如如下代碼:安全
class UnsafeStates{ private String[] states = new String[]{"AK", "AL"}; //states變量做用域是private而咱們在getStates方法中卻把它發佈了, //這樣就稱爲數組states逸出了它所在的做用域。 public String[] getStates(){ return states; } public static void main(String[] args) { UnsafeStates unsafeStates = new UnsafeStates(); //此處咱們經過getStates方法對private修飾的states作出更改。(對象逸出了) unsafePublish.getStates()[0] = "AA"; } }
咱們再來看看更加隱祕的this逸出,那麼什麼是this逸出?觀察如下代碼:多線程
public class ThisEscape{ private int value; public ThisEscape(EventSource source){ source.registerListener{ //當事件監聽類註冊完畢後, //實際上咱們已經將EventListener匿名內部類發佈出去了 //而此時咱們的一些初始化工做尚未完成 //也就是一個類尚未構造完成已經將對象發佈出去了 new EventListener(){ public void onEvent(Event e){ doSomething(e); } } } //一些初始化工做 value = 7; } public void doSomething(Event e){ System.out.println(value);//this對象逸出,有可能在構造方法初始化的時候一些初始工做未完成而提早發佈了對象從而致使對象逸出的問題 } }
線程不安全的,在多線程環境下若是同時有多個線程經過getInstance建立SingletonExample1實例,有可能致使SingletonExample1實例的屢次建立,影響程序邏輯。併發
/** * 懶漢模式 * 單例實例在第一次使用時進行建立 */ public class SingletonExample1 { // 私有構造函數 private SingletonExample1() { } // 單例對象 private static SingletonExample1 instance = null; // 靜態的工廠方法 public static SingletonExample1 getInstance() { if (instance == null) { instance = new SingletonExample1(); } return instance; } }
線程安全的,在SingletonExample2首次加載時建立對象。函數
/** * 餓漢模式 * 單例實例在類裝載時進行建立 */ public class SingletonExample2 { // 私有構造函數 private SingletonExample2() { } // 單例對象 private static SingletonExample2 instance = new SingletonExample2(); // 靜態的工廠方法 public static SingletonExample2 getInstance() { return instance; } }
線程不安全的,雙重檢測機制通常不會發生線程安全問題,可是不可避免有可能會出現線程安全問題,好比發生了指令重排。高併發
/** * 懶漢模式 -》 雙重同步鎖單例模式 * 單例實例在第一次使用時進行建立 */ public class SingletonExample4 { // 私有構造函數 private SingletonExample4() { } // 一、memory = allocate() 分配對象的內存空間 // 二、ctorInstance() 初始化對象 // 三、instance = memory 設置instance指向剛分配的內存 // JVM和cpu優化,發生了指令重排。有可能會致使還未初始化完成的對象已經被別的線程所使用。 // 一、memory = allocate() 分配對象的內存空間 // 三、instance = memory 設置instance指向剛分配的內存 // 二、ctorInstance() 初始化對象 // 單例對象 private static SingletonExample4 instance = null; // 靜態的工廠方法 public static SingletonExample4 getInstance() { if (instance == null) { // 雙重檢測機制 // B synchronized (SingletonExample4.class) { // 同步鎖 if (instance == null) { instance = new SingletonExample4(); // A - 3 } } } return instance; } }
線程安全的,volatile會禁止指令重排性能
/** * 懶漢模式 -》 雙重同步鎖單例模式 * 單例實例在第一次使用時進行建立 */ @ThreadSafe public class SingletonExample5 { // 私有構造函數 private SingletonExample5() { } // 一、memory = allocate() 分配對象的內存空間 // 二、ctorInstance() 初始化對象 // 三、instance = memory 設置instance指向剛分配的內存 // 單例對象 volatile + 雙重檢測機制 -> 禁止指令重排 private volatile static SingletonExample5 instance = null; // 靜態的工廠方法 public static SingletonExample5 getInstance() { if (instance == null) { // 雙重檢測機制 // B synchronized (SingletonExample5.class) { // 同步鎖 if (instance == null) { instance = new SingletonExample5(); // A - 3 } } } return instance; } }
線程安全的,但並不推薦,由於每次經過getInstance獲取對象時都會加鎖,而影響程序在高併發下的性能。優化
// 私有構造函數 private SingletonExample3() { } // 單例對象 private static SingletonExample3 instance = null; // 靜態的工廠方法 public static synchronized SingletonExample3 getInstance() { if (instance == null) { instance = new SingletonExample3(); } return instance; }
線程安全而且是推薦的寫法。this
/** * 枚舉模式:最安全 */ public class SingletonExample7 { // 私有構造函數 private SingletonExample7() { } public static SingletonExample7 getInstance() { return Singleton.INSTANCE.getInstance(); } private enum Singleton { INSTANCE; private SingletonExample7 singleton; // JVM保證這個方法絕對只調用一次 Singleton() { singleton = new SingletonExample7(); } public SingletonExample7 getInstance() { return singleton; } } }
線程安全的,推薦使用的一種建立單線程的方式
/** * 懶漢式,靜態內部類的方式 */ public class Singleton { private static class LazyHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton() { } public static final Singleton getInstance() { return LazyHolder.INSTANCE; } }