餓漢式缺點: 可能會浪費記憶體 執行緒安全
懶漢式缺點:只有在單執行緒下才有效 多執行緒下會破壞單例模式 執行緒不安全
餓漢是執行緒安全的,我們在這不討論他.
主角是我們的懶漢式!!
public class LazyMan{
//私有化構造方法
private LazyMan(){
}
//創建一個物件 不賦值
private static LazyMan lazyMan;
//對外提供方法
public static LazyMan getInstance(){
if(lazyMan==null){//(1)
try{
//為了更好的體現 多執行緒 睡眠使所有執行緒都進入
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
lazyMan=new LazyMan();
}
return lazyMan;
}
public static void main(String[] args){
for(int i=0;i<10;i++){
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
結果==> 會創建10個物件
懶漢式執行緒不安全的原因:
執行緒不安全的原因:
假設有兩個執行緒A和B A執行緒到達(1)處時判斷lazyMan物件為null此時cpu的執行權被執行緒B搶去
但A還沒有對lazyMan進行實體化
B讀取lazyMan物件的狀態仍然是null 執行緒AB都會進入if中通過new 創建兩個不同的物件
解決方案:
執行緒不安全 就給它加鎖
private volatile static LazyMan2 lazyMan;
//解決方案 雙重檢測鎖的懶漢式單例 DCL
public static LazyMan1 getInstance() {
if (lazyMan == null) {
synchronized (LazyMan1.class) {
if (lazyMan == null) {// (1)處
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lazyMan = new LazyMan1(); // 不是原子性操作
/*
* 1.分配記憶體空間
* 2.執行構造方法 初始化物件
* 3.把這個物件指向這個空間
* volatile只保證可見性 不保證原子性的,但是可以防止指令重排
* */
}
}
}
return lazyMan;
}
還可以用反射暴力破解
public static void main(String[] args) throws Exception {
//反射
LazyMan2 instance1 = new LazyMan2();
Constructor<LazyMan> declaredConstructor =
LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true); //無視私有的構造器
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
都會使得懶漢式的單例被破壞 創建多個物件!
##列舉也是執行緒安全的 即便使用反射也不能破壞其單例模式!!!
列舉的不能破壞單例的方法這里不提.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/22984.html
標籤:其他
