1. 程式人生 > 其它 >log4j2實現日誌脫敏

log4j2實現日誌脫敏

單例模式

  定義:確保一個類最多隻有一個例項,並提供一個全域性訪問點

  單例模式可以分為兩種:預載入和懶載入

1.1 預載入
  顧名思義,就是預先載入。再進一步解釋就是還沒有使用該單例物件,但是,該單例物件就已經被載入到記憶體了。

  public class PreloadSingleton {
    public static PreloadSingleton instance = new PreloadSingleton();
    /其他的類無法例項化單例類的物件
    private PreloadSingleton() {
    };
    public static PreloadSingleton getInstance() {
      return instance;
    }
  }
  很明顯,沒有使用該單例物件,該物件就被載入到了記憶體,會造成記憶體的浪費。

1.2 懶載入
  為了避免記憶體的浪費,我們可以採用懶載入,即用到該單例物件的時候再建立。

  public class Singleton {
    private static Singleton instance=null;
    private Singleton(){
    };

    public static Singleton getInstance()
    {
      if(instance==null)
      {
        instance=new Singleton();
      }
      return instance;
    }
  }  
1.3 單例模式和執行緒安全
  (1)預載入只有一條語句return instance,這顯然可以保證執行緒安全。但是,我們知道預載入會造成記憶體的浪費。

  (2)懶載入不浪費記憶體,但是無法保證執行緒的安全。首先,if判斷以及其記憶體執行程式碼是非原子性的。其次,new Singleton()無法保證執行的順序性。

    不滿足原子性或者順序性,執行緒肯定是不安全的,這是基本的常識,不再贅述。我主要講一下為什麼new Singleton()無法保證順序性。我們知道建立一個物件分三步:

    memory=allocate();//1:初始化記憶體空間

    ctorInstance(memory);//2:初始化物件

    instance=memory();//3:設定instance指向剛分配的記憶體地址


    jvm為了提高程式執行效能,會對沒有依賴關係的程式碼進行重排序,上面2和3行程式碼可能被重新排序。我們用兩個執行緒來說明執行緒是不安全的。執行緒A和執行緒B都建立物件。其中,A2和A3的重排序,將導致執行緒B在B1處判斷出instance不為空,執行緒B接下來將訪問instance引用的物件。此時,執行緒B將會訪問到一個還未初始化的物件(執行緒不安全)。

 

 

1.4 保證懶載入的執行緒安全
  我們首先想到的就是使用synchronized關鍵字。synchronized載入getInstace()函式上確實保證了執行緒的安全。但是,如果要經常的呼叫getInstance()方法,不管有沒有初始化例項,都會喚醒和阻塞執行緒。為了避免執行緒的上下文切換消耗大量時間,如果物件已經例項化了,我們沒有必要再使用synchronized加鎖,直接返回物件。

  public class Singleton {
    private static Singleton instance = null;
    private Singleton() {
    };
    public static synchronized Singleton getInstance() {
      if (instance == null) {
        instance = new Singleton();
      }
      return instance;
    }
  }
  我們把sychronized加在if(instance==null)判斷語句裡面,保證instance未例項化的時候才加鎖

  public class Singleton {
    private static Singleton instance = null;
    private Singleton() {
    };
    public static synchronized Singleton getInstance() {
      if (instance == null) {
        synchronized (Singleton.class) {
          if (instance == null) {
            instance = new Singleton();
          }
        }
      }
      return instance;
    }
  }
  我們經過1.3的討論知道new一個物件的程式碼是無法保證順序性的,因此,我們需要使用另一個關鍵字volatile保證物件例項化過程的順序性。

  public class Singleton {
    private static volatile Singleton instance = null;
    private Singleton() {
    };
    public static synchronized Singleton getInstance() {
      if (instance == null) {
        synchronized (instance) {
          if (instance == null) {
            instance = new Singleton();
          }
        }
      }
      return instance;
    }
  }
  到此,我們就保證了懶載入的執行緒安全。