基于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

怎么保护单例不被破坏

  • 反射的解决方式
    在这里插入图片描述
  • 反序列化的解决方式
    反序列化生成实例,并不会调用当前类的构造方法,并不能通过处理反射的方式来解决。
    jdk提供了一种方式,让单例类增加一个成员方法即可解决。方法如下

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

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

  • 克隆的解决方式
    可以仿照上面的处理方式
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值