設計模式(一)單例模式
阿新 • • 發佈:2021-07-05
餓漢式,DCL懶漢式
1、餓漢式單例
/** * 餓漢式單例 * @author it-小林 * @date 2021年07月05日 9:05 */ public class HungryPattern { //可能會浪費空間,開闢了空間,卻沒有使用 private HungryPattern(){ } private final static HungryPattern hungryPattern = new HungryPattern(); public final HungryPattern getInstance(){ returnhungryPattern; } }
2、餓漢式單例
(1)存在多執行緒併發模式,也就是說多執行緒模式下,會有問題。
1 /** 2 * @author it-小林 3 * @date 2021年07月05日 9:11 4 */ 5 public class LazyPattern { 6 7 private LazyPattern() { 8 9 } 10 11 private static LazyPattern lazyPattern; 12 13 private static LazyPattern getInstance(){14 if(lazyPattern == null){ 15 lazyPattern = new LazyPattern(); 16 } 17 return lazyPattern; 18 } 19 20 //多執行緒開發 21 public static void main(String[] args) { 22 Thread thread1 = new Thread(){ 23 @Override 24 public void run() {25 System.out.println(LazyPattern.getInstance()); 26 } 27 }; 28 thread1.start(); 29 Thread thread2 = new Thread(){ 30 @Override 31 public void run() { 32 System.out.println(LazyPattern.getInstance()); 33 } 34 }; 35 thread2.start(); 36 } 37 }
(2)雙重檢測鎖模式的 懶漢式單例 DCL懶漢式
注意:synchronized解決併發問題,但是因為lazyMan = new LazyMan();
不是原子性操作(可以分割,見程式碼註釋),可能發生指令重排序的問題,通過volatil來解決。
-
Java 語言提供了 volatile和 synchronized 兩個關鍵字來保證執行緒之間操作的有序性,volatile 是因為其本身包含“禁止指令重排序”的語義,synchronized 是由“一個變數在同一個時刻只允許一條執行緒對其進行 lock 操作”這條規則獲得的,此規則決定了持有同一個物件鎖的兩個同步塊只能序列執行;
- 原子性就是指該操作是不可再分的。不論是多核還是單核,具有原子性的量,同一時刻只能有一個執行緒來對它進行操作。簡而言之,在整個操作過程中不會被執行緒排程器中斷的操作,都可認為是原子性。比如 a = 1。
/** * @author it-小林 * @date 2021年07月05日 9:11 */ public class LazyPattern { private LazyPattern() { } private static LazyPattern lazyPattern; //雙重檢測模式的 懶漢式單例 DCL懶漢式 private static LazyPattern getInstance(){ if(lazyPattern == null){ synchronized (LazyPattern.class){ if(lazyPattern == null){ //不是一個原子性操作 lazyPattern = new LazyPattern(); /** * 1.分配記憶體空間 * 2、執行構造方法,初始化物件 * 3、把這個物件指向這個空間 */ } } } return lazyPattern; } //多執行緒開發 public static void main(String[] args) { Thread thread1 = new Thread(){ @Override public void run() { System.out.println(LazyPattern.getInstance()); } }; thread1.start(); Thread thread2 = new Thread(){ @Override public void run() { System.out.println(LazyPattern.getInstance()); } }; thread2.start(); } }
3、靜態內部類
1 /** 2 * 靜態內部類 3 * @author it-小林 4 * @date 2021年07月05日 19:29 5 */ 6 public class Holder { 7 8 //構造器私有 9 public Holder() { 10 11 } 12 13 public static Holder getInstance(){ 14 return InnerClass.HOLDER; 15 } 16 17 public static class InnerClass{ 18 private static final Holder HOLDER = new Holder(); 19 } 20 21 }
4、單例不安全,反射破壞(見註釋及main方法中反射破解步驟)
1 /** 2 * @author 林瑞濤 3 * @date 2021年07月05日 9:11 4 */ 5 public class LazyPattern { 6 7 //紅綠等解決通過反射建立物件(反編譯可以破解該方法) 8 private static boolean bolNew = false; 9 private LazyPattern() { 10 synchronized (LazyPattern.class){ 11 if(bolNew == false){ 12 bolNew = true; 13 }else{ 14 throw new RuntimeException("不要試圖使用反射破壞單例"); 15 } 16 } 17 } 18 19 //volatile避免指令重排 20 private volatile static LazyPattern lazyPattern; 21 22 23 //雙重檢測模式的 懶漢式單例 DCL懶漢式 24 private static LazyPattern getInstance(){ 25 26 if(lazyPattern == null){ 27 //不是一個原子性操作 28 lazyPattern = new LazyPattern(); 29 } 30 return lazyPattern; 31 } 32 33 //反射 34 public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 35 Field bolNew = LazyPattern.class.getDeclaredField("bolNew"); 36 bolNew.setAccessible(true); 37 38 Constructor<LazyPattern> declaredConstructor = LazyPattern.class.getDeclaredConstructor(null); 39 declaredConstructor.setAccessible(true);//無視私有的構造器 40 LazyPattern instance1 = declaredConstructor.newInstance(); 41 bolNew.set(instance1,false); 42 System.out.println(instance1); 43 LazyPattern instance2 = declaredConstructor.newInstance(); 44 45 System.out.println(instance2); 46 } 47 }
列舉:通過反射破解枚舉發現不成功:
1、普通的反編譯會欺騙開發者,說enum列舉是無參構造
2、實際enum為有參構造(見後面);
3、通過反射破解列舉會發現丟擲異常
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:417) at com.ph.single.Test.main(EnumSingle.java:19)
1 //enmu是什麼?本身也是一個class類 2 public enum EnumSingle { 3 INSTANCE; 4 public EnumSingle getInstance(){ 5 return INSTANCE; 6 } 7 } 8 9 class Test{ 10 public static void main(String[] args) throws Exception { 11 EnumSingle instance = EnumSingle.INSTANCE; 12 Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class); 13 declaredConstructor.setAccessible(true); 14 EnumSingle instance2 = declaredConstructor.newInstance(); 15 16 //java.lang.NoSuchMethodException: com.ph.single.EnumSingle.<init>() 17 System.out.println(instance); 18 System.out.println(instance2); 19 20 } 21 }
通過idea和jdk自帶的反編譯列舉如下:
通過JDK反編譯列舉的程式碼如下,發現列舉其實是有參構造
初出茅廬,多多指教