1. 程式人生 > >單例的幾種方式,以及如何破壞單例,使用枚舉保護單例;

單例的幾種方式,以及如何破壞單例,使用枚舉保護單例;

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      * @return
Singleton @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 } 31
lock.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 }

單例的幾種方式,以及如何破壞單例,使用枚舉保護單例;