private AAA(){
}
1 餓漢式 (線程安全,不能延遲實例化,浪費內存空間)
private static AAA aaa = new AAA();
public static AAA getInstance(){
return aaa;
}
2 懶漢式 (線程安全,延遲實例化)
private static AAA instance;
public static synchronized AAA getInstance(){
if(instance==null){
instance = new AAA();
}
return instance;
}
3 Double Check Lock(雙重鎖機制,線程安全,延遲實例化,效率高)
private static volatile AAA instance;
public static AAA getInstance(){
if(instance == null){
synchronized (AAA.class){
if (instance ==null){
instance = new AAA();
}
}
}
return instance;
}
關於此方式中的volatile作用
(1)禁止指令重排序
主要是因爲
instance = new AAA();不是一個原子操作
下面這個鏈接清楚的解釋了指令重排序的問題。
https://www.jianshu.com/p/1c72a061b40e
(2)保證可見性
線程A在自己工作線程中創建了實例,但還未同步到主存中,線程B判斷主存中instance還是null,那麼線程B又將在自己工作線程中創建一個實例,這樣就會創建多個實例。
4 靜態內部類(線程安全,延遲實例化,效率高)
private static class BBB{
private static final AAA aaa = new AAA();
}
public static AAA getInstance(){
return BBB.aaa;
}
靜態內部類的加載方式:
內部靜態類不會自動初始化,只有調用靜態內部類的方法,靜態域,或者構造方法的時候纔會加載靜態內部類。
5、枚舉(線程安全,調用效率高,不能延時加載,可以天然的防止反射和反序列化調用)
舉個栗子:一旦你實現了serializable接口,他們就不再是單例的了,因爲readObject()方法總是返回一個 新的實例對象,就像java中的構造器一樣。
public enum BBC { //枚舉元素本身就是單例 INSTANCE; private BBC() { } //添加自己需要的操作 public void singletonOperation(){ } }
/** * 使用方法 */ boolean bl = BBC.INSTANCE.singletonOperation();
枚舉完成的單例有一下幾個優點:
1,線程安全
2,自由序列化
3,即使使用反射機制也無法多次實例化一個枚舉量