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;
}
}
到此,我們就保證了懶載入的執行緒安全。