1. 程式人生 > 其它 >關於listener無法啟動的問題解決

關於listener無法啟動的問題解決

  1. 懶漢式-執行緒不安全
    以下實現中,私有靜態變數 uniqueInstance 被延遲例項化,這樣做的好處是,如果沒有用到該類,那麼就不會例項化 uniqueInstance,從而節約資源。
    這個實現在多執行緒環境下是不安全的,如果多個執行緒能夠同時進入 if (uniqueInstance == null) ,並且此時 uniqueInstance 為 null,那麼會有多個執行緒執行 uniqueInstance = new Singleton(); 語句,這將導致多次例項化 uniqueInstance。
public class Singleton {

    private static Singleton uniqueInstance;

    private Singleton() {
    }

    public static Singleton getUniqueInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}
  1. 餓漢式-執行緒安全
    執行緒不安全問題主要是由於 uniqueInstance 被多次例項化,採取直接例項化 uniqueInstance 的方式就不會產生執行緒不安全問題。 但是直接例項化的方式也丟失了延遲例項化帶來的節約資源的好處。

private static Singleton uniqueInstance = new Singleton();

  1. 懶漢式-執行緒安全
    只需要對 getUniqueInstance() 方法加鎖,那麼在一個時間點只能有一個執行緒能夠進入該方法,從而避免了多次例項化 uniqueInstance 的問題。 但是當一個執行緒進入該方法之後,其它試圖進入該方法的執行緒都必須等待,因此效能上有一定的損耗。
public static synchronized Singleton getUniqueInstance() {
    if (uniqueInstance == null) {
        uniqueInstance = new Singleton();
    }
    return uniqueInstance;
}
  1. 雙重校驗鎖-執行緒安全
    uniqueInstance 只需要被例項化一次,之後就可以直接使用了。加鎖操作只需要對例項化那部分的程式碼進行,只有當 uniqueInstance 沒有被例項化時,才需要進行加鎖。 雙重校驗鎖先判斷 uniqueInstance 是否已經被例項化,如果沒有被例項化,那麼才對例項化語句進行加鎖。
public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public static Singleton getUniqueInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

考慮下面的實現,也就是隻使用了一個 if 語句。在 uniqueInstance == null 的情況下,如果兩個執行緒同時執行 if 語句,那麼兩個執行緒就會同時進入 if 語句塊內。雖然在 if 語句塊內有加鎖操作,但是兩個執行緒都會執行 uniqueInstance = new Singleton(); 這條語句,只是先後的問題,那麼就會進行兩次例項化,從而產生了兩個例項。因此必須使用雙重校驗鎖,也就是需要使用兩個 if 語句。

if (uniqueInstance == null) {
    synchronized (Singleton.class) {
        uniqueInstance = new Singleton();
    }
}

uniqueInstance 採用 volatile 關鍵字修飾也是很有必要的。uniqueInstance = new Singleton(); 這段程式碼其實是分為三步執行。

  • 分配記憶體空間
  • 初始化物件
  • 將 uniqueInstance 指向分配的記憶體地址

但是由於 JVM 具有指令重排的特性,有可能執行順序變為了 1>3>2,這在單執行緒情況下自然是沒有問題。但如果是多執行緒下,有可能獲得是一個還沒有被初始化的例項,以致於程式出錯。 使用 volatile 可以禁止 JVM 的指令重排,保證在多執行緒環境下也能正常執行。

  1. 靜態內部類實現
    當 Singleton 類載入時,靜態內部類 SingletonHolder 沒有被載入進記憶體。只有當呼叫 getUniqueInstance() 方法從而觸發 SingletonHolder.INSTANCE 時 SingletonHolder 才會被載入,此時初始化 INSTANCE 例項。 這種方式不僅具有延遲初始化的好處,而且由虛擬機器提供了對執行緒安全的支援。
public class Singleton {

    private Singleton() {
    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getUniqueInstance() {
        return SingletonHolder.INSTANCE;
    }
}
  1. 列舉實現
    這是單例模式的最佳實踐,它實現簡單,並且在面對複雜的序列化或者反射攻擊的時候,能夠防止例項化多次。
public enum Singleton {
    uniqueInstance;
}