设为首页 加入收藏

TOP

设计模式 - 单例模式之多线程调试与破坏单例(二)
2019-10-10 11:17:42 】 浏览:502
Tags:设计模式 单例 模式 线程 调试 破坏
c.newInstance(); LazyInnerClassSingleton o2 = c.newInstance(); // 只要 o1和o2 地址不相等,就可以说明这是两个不同的对象,也就是违背了单例模式的初衷 System.out.println(o1 == o2); } catch (Exception e) { e.printStackTrace(); } }

运行结果如下:

显然,是创建了两个不同的实例。现在,我们在其构造方法中做一些限制,一旦出现多次重复创建,则直接抛出异常。来看优化后的代码:

public class LazyInnerClassSingleton {

    private LazyInnerClassSingleton() {
        if(LazyHolder.INSTANCE != null){
            throw new RuntimeException("不允许创建多个实例");
        }
    }

    // 注意关键字final,保证方法不被重写和重载
    public static final LazyInnerClassSingleton getInstance() {
        return LazyHolder.INSTANCE;
    }

    private static class LazyHolder {
        // 注意 final 关键字(保证不被修改)
        private static final LazyInnerClassSingleton INSTANCE = new LazyInnerClassSingleton();
    }
}

再次调用:

至此,就避免了单例被反射破坏的问题。

序列化破坏单例

另外一种情况,可能会遇到,我们需要将对象序列化到磁盘,下次使用时再从磁盘反序列化回来,反序列化的对象会被重新分配内存,那如果序列化的对象为单例,则就违背了单例模式的初衷。这也相当于破坏了单例。

演示

我们还是以LazyInnerClassSingleton为例,将LazyInnerClassSingleton 实现 Serializable 接口;

然后编写测试代码:

/**
 * @author eamon.zhang
 * @date 2019-10-08 下午3:06
 */
public class SerializableTest {
    public static void main(String[] args) {
        LazyInnerClassSingleton s1 = null;
        LazyInnerClassSingleton s2 = LazyInnerClassSingleton.getInstance();

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("LazyInnerClassSingleton.obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(s2);
            oos.flush();
            oos.close();

            FileInputStream fis = new FileInputStream("LazyInnerClassSingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            s1 = (LazyInnerClassSingleton)ois.readObject();
            ois.close();

            System.out.println(s1);
            System.out.println(s2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行测试代码:

可以看到,结果为两个不同的对象。这同样违背了单例模式的初衷。那么我们如何保证序列化的情况也能实现单例呢?其实也很简单,使用 readResolve() 方法即可:

public class LazyInnerClassSingleton implements Serializable {

    private LazyInnerClassSingleton() {
        if (LazyHolder.INSTANCE != null) {
            throw new RuntimeException("不允许创建多个实例");
        }
    }

    // 注意关键字final,保证方法不被重写和重载
    public static final LazyInnerClassSingleton getInstance() {
        return LazyHolder.INSTANCE;
    }

    private static class LazyHolder {
        // 注意 final 关键字(保证不被修改)
        private static final LazyInnerClassSingleton INSTANCE = new LazyInnerClassSingleton();
    }

    // 解决反序列化对象不一致问题
    private Object readResolve() {
        return LazyHolder.INSTANCE;
    }
}

大家肯定会问,why?

为了一探究竟,我们来看一下 JDK 源码,我们进入 ObjectInputStream 类的 readObject()方法:

public final Object readObject() throws IOException, ClassNotFoundException {
        if (this.enableOverride) {
            return this.readObjectOverride();
        } else {
            int outerHandle = this.passHandle;

            Object var4;
            try {
                Object obj = this.readObject0(false);
                this.handles.markDependency(outerHandle, this.passHandle);
                ClassNotFoundException ex = this.handles.lookupException(this.passHandle);
                if (ex != null) {
                    throw ex;
                }

                if (this.depth == 0L) {
                    this.vlist.doCallbacks();
                    this.freeze();
                }

                var4 = obj;
            } finally {
                this.passHandle = outerHandle;
                if (this.closed && this.depth == 0L) {
                    this.clear();
                }

            }

            return var4;
        }
    }

我们发现:readObject 中又调用了我们重写的 readObject0()方法,进入 readObject0()方法:

private Object readObject0(boolean unshared) throws IOException {
        ...
        try {
            switch(tc) {
            ...
            case 115:
                var4 = this.checkResolve(this.readOrdinaryObject(unshared));
                return
首页 上一页 1 2 3 4 5 下一页 尾页 2/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇设计模式-行为型-访问者模式 下一篇通俗易懂设计模式解析——迭代器..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目