单例的几种方式,以及如何破坏单例,使用枚举保护单例;

Posted 拾花酿春

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例的几种方式,以及如何破坏单例,使用枚举保护单例;相关的知识,希望对你有一定的参考价值。

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 }

 

以上是关于单例的几种方式,以及如何破坏单例,使用枚举保护单例;的主要内容,如果未能解决你的问题,请参考以下文章

设计模式单例模式

单例模式--反射--防止序列化破坏单例模式

从单例模式开始

单例模式的创建破坏和防破坏

枚举、单例和反序列化

单例模式