單例的幾種方式,以及如何破壞單例,使用枚舉保護單例;
阿新 • • 發佈:2017-12-27
des new 如果 auth 變量 ins 方式 break 時機
1、基礎入門單例:
1 public class Singleton { 2 private Singleton(){ 3 System.out.println("Singleton 初始化的過程"); 4 } 5 6 private static Singleton singleton = null; 7 8 /**@Description: 單例同步控制,方法加syn 9 * @return 10 * @author BIQI 2017年12月20日下午3:10:23 11 * @returnSingleton @throws 12 */ 13 public static synchronized Singleton getSingleton1(){ 14 if (null == singleton ) { 15 singleton = new Singleton(); 16 } 17 return singleton; 18 } 19 20 /**@Description: 單例同步控制2,使用ReentrantLock鎖 21 * @return 22 *@author BIQI 2017年12月20日下午3:14:16 23 * @return Singleton @throws 24 */ 25 public static Singleton getSingleton2(){ 26 ReentrantLock lock = new ReentrantLock(); 27 lock.lock(); 28 if (null == singleton ) { 29 singleton = new Singleton(); 30 } 31lock.unlock(); 32 return singleton; 33 } 34 35 /**@Description: 雙重檢測機制 DCL(Double CheckLock)實現單例 36 * @return 37 * @author BIQI 2017年12月20日下午3:15:31 38 * @return Singleton @throws 39 */ 40 public static Singleton getSingleton3(){ 41 //第一重檢測 42 if (null == singleton ) { 43 synchronized (singleton) { 44 //第二重檢測 45 if (null == singleton) { 46 singleton = new Singleton(); 47 } 48 } 49 } 50 return singleton; 51 } 52 }
2、單例的進階,控制指令重排
public class Singleton2 { /* * 指令重排是 比如java中簡單的一句 instance = new Singleton,會被編譯器編譯成如下JVM指令: memory =allocate(); //1:分配對象的內存空間 ctorInstance(memory); //2:初始化對象 instance =memory; //3:設置instance指向剛分配的內存地址 但是這些指令順序並非一成不變,有可能會經過JVM和CPU的優化,指令重排成下面的順序: memory =allocate(); //1:分配對象的內存空間 instance =memory; //3:設置instance指向剛分配的內存地址 ctorInstance(memory); //2:初始化對象 */ private Singleton2() { System.out.println("Singleton 初始化的過程"); } //volatile 阻止變量訪問前後指令的重排,保證指令執行順序 private volatile static Singleton2 singleton = null; //單例對象 //靜態工廠方法 public static Singleton2 getInstance() { if (singleton == null) { //雙重檢測機制 synchronized (Singleton.class){ //同步鎖 if (singleton == null) { //雙重檢測機制 singleton = new Singleton2(); } } } return singleton; } }
3、classloader的加載機制來實現懶加載單例實現
1 public class Singleton3 { 2 /** 3 * 1.從外部無法訪問靜態內部類LazyHolder, 4 * 只有當調用Singleton.getInstance方法的時候, 5 * 才能得到單例對象INSTANCE。 6 * 2.INSTANCE對象初始化的時機並不是在單例類Singleton被加載的時候, 7 * 而是在調用getInstance方法,使得靜態內部類LazyHolder被加載的時候。 8 * 因此這種實現方式是利用classloader的加載機制來實現懶加載, 9 * 並保證構建單例的線程安全。 10 * 11 */ 12 private static class LazyHolder { 13 private static final Singleton3 INSTANCE = new Singleton3(); 14 } 15 private Singleton3 (){} 16 public static Singleton3 getInstance() { 17 return LazyHolder.INSTANCE; 18 } 19 }
4、最推薦的單例,使用枚舉,因為枚舉的特性以及實現方式(這裏不闡述);
1 public enum SingletonEnum1 { 2 3 INSTANCE(); 4 5 SingletonEnum1(){ 6 System.out.println("SingletonEnum1 初始化的過程"); 7 } 8 }
5、如何破壞單例模式,如果不適用枚舉的話;
1 /** 2 * @Title: BreakSingleton.java 3 * @Description: 破壞單例的情況,後面通過枚舉去破解 4 * 5 * @author BIQI IS BEST 6 * @date 2017年12月20日 下午3:27:39 7 * @version V1.0 8 */ 9 public class BreakSingleton { 10 11 public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { 12 // TODO Auto-generated method stub 13 14 //獲得構造器 15 Constructor con = Singleton.class.getDeclaredConstructor(); 16 //設置為可訪問 17 con.setAccessible(true); 18 //構造兩個不同的對象 19 Singleton singleton1 = (Singleton)con.newInstance(); 20 Singleton singleton2 = (Singleton)con.newInstance(); 21 //驗證是否是不同對象 22 System.out.println(singleton1.equals(singleton2)); 23 24 //枚舉類的獲得 25 SingletonEnum1 singletest1 =SingletonEnum1.INSTANCE; 26 SingletonEnum1 singletest2 =SingletonEnum1.INSTANCE; 27 System.out.println(singletest1.equals(singletest2)); 28 } 29 }
單例的幾種方式,以及如何破壞單例,使用枚舉保護單例;