基于DCL实现的单例,有几种方式可以破坏

破坏单例的方式

  • 反射
  • 序列化与反序列化
  • 克隆

代码实现

public class DCSingleton implements Serializable, Cloneable {
    /**
     * 这里加入volatile修饰,利用的volatile的有序性
     */
    private static volatile DCSingleton singleton;

    /**
     * 禁止外部代码用new构造实例
     */
    private DCSingleton() {

    }

    public static DCSingleton getInstance() {
        /**
         * 第一次检查
         */
        if (singleton == null) {
            synchronized (DCSingleton.class) {
                /**
                 * 第二次检查,可以思考一下为什么需要进行第二次检查
                 */
                if (singleton == null) {
                    /**
                     * volatile关键字作用所在
                     * = new 这两个动作并不是原子操作,为线程不安全,在《java并发编程的艺术》此书上有详细介绍
                     */
                    singleton = new DCSingleton();
                }
            }
        }
        return singleton;
    }


    @Override
    public Object clone() throws CloneNotSupportedException {
        Object object = super.clone();
        return object;
    }


}

@Slf4j
public class TestSingleton {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, IOException, CloneNotSupportedException, InterruptedException {
        final  CountDownLatch countDownLatch = new CountDownLatch(5);
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.info("实例={}", DCSingleton.getInstance());
                    countDownLatch.countDown();
                }
            }).start();
        }
        countDownLatch.await();
        DCSingleton singleton = DCSingleton.getInstance();

        Class clazz = Class.forName(singleton.getClass().getName());
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        log.info("反射构造的实例={}", constructor.newInstance());


        File file = new File("serial.ser");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
        objectOutputStream.writeObject(singleton);
        objectOutputStream.flush();
        objectOutputStream.close();
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
        DCSingleton deSerialSingleton = (DCSingleton) objectInputStream.readObject();
        log.info("反序列化后的实例={}",deSerialSingleton);

        DCSingleton cloneSingleton = (DCSingleton) singleton.clone();
        log.info("克隆后得到的实例={}",cloneSingleton);

    }
}

执行结果
10:21:13.355 [Thread-4] INFO com.kk.singleton.TestSingleton - 实例=com.kk.singleton.doubleCheck.DCSingleton@3868223f
10:21:13.355 [Thread-2] INFO com.kk.singleton.TestSingleton - 实例=com.kk.singleton.doubleCheck.DCSingleton@3868223f
10:21:13.355 [Thread-1] INFO com.kk.singleton.TestSingleton - 实例=com.kk.singleton.doubleCheck.DCSingleton@3868223f
10:21:13.355 [Thread-0] INFO com.kk.singleton.TestSingleton - 实例=com.kk.singleton.doubleCheck.DCSingleton@3868223f
10:21:13.355 [Thread-3] INFO com.kk.singleton.TestSingleton - 实例=com.kk.singleton.doubleCheck.DCSingleton@3868223f
10:21:13.363 [main] INFO com.kk.singleton.TestSingleton - 反射构造的实例=com.kk.singleton.doubleCheck.DCSingleton@1a6c5a9e
10:21:13.388 [main] INFO com.kk.singleton.TestSingleton - 反序列化后的实例=com.kk.singleton.doubleCheck.DCSingleton@49c2faae
10:21:13.388 [main] INFO com.kk.singleton.TestSingleton - 克隆后得到的实例=com.kk.singleton.doubleCheck.DCSingleton@20ad9418

怎么保护单例不被破坏

  • 反射的解决方式
    基于DCL实现的单例,有几种方式可以破坏_第1张图片
  • 反序列化的解决方式
    反序列化生成实例,并不会调用当前类的构造方法,并不能通过处理反射的方式来解决。
    jdk提供了一种方式,让单例类增加一个成员方法即可解决。方法如下

    private  Object readResolve() throws ObjectStreamException{
        return singleton;
    }

具体原因可以去查看Object输入输出流的源码。
反序列化虽然不会调用被反序列化的类的构造函数,但是会调用其超类的构造函数,我们也可以根据这个思路去解决反序列破坏单例的问题,但这里不做推荐。

  • 克隆的解决方式
    可以仿照上面的处理方式

你可能感兴趣的:(设计模式)