设为首页 加入收藏

TOP

设计模式 - 单例模式之多线程调试与破坏单例(三)
2019-10-10 11:17:42 】 浏览:497
Tags:设计模式 单例 模式 线程 调试 破坏
var4; ... } finally { --this.depth; this.bin.setBlockDataMode(oldMode); } return var4; }

我们看到代码中调用了 ObjectInputStreamreadOrdinaryObject() 方法,我们继续进入看源码:

private Object readOrdinaryObject(boolean unshared) throws IOException {
        ...
            if (cl != String.class && cl != Class.class && cl != ObjectStreamClass.class) {
                Object obj;
                try {
                    obj = desc.isInstantiable() ? desc.newInstance() : null;
                } catch (Exception var7) {
                    throw (IOException)(new InvalidClassException(desc.forClass().getName(), "unable to create instance")).initCause(var7);
                }

        ...

        }
    }

发现调用了 ObjectStreamClassisInstantiable()方法,而 isInstantiable()里面的代码如下:

boolean isInstantiable() {
    this.requireInitialized();
    return this.cons != null;
}

代码非常简单,就是判断一下构造方法是否为空,构造方法不为空就返回 true,也就是说,只要有无参构造方法就会实例化;这时候,其实还没有找到为什么加上readResolve()方法就避免了单例被破坏的真正原因,我们再次回到ObjectInputStreamreadOrdinaryObject()方法继续往下看可以找到如下代码:

private Object readOrdinaryObject(boolean unshared) throws IOException {
    ...
    if (obj != null && this.handles.lookupException(this.passHandle) == null && desc.hasReadResolveMethod()) {
        Object rep = desc.invokeReadResolve(obj);
        if (unshared && rep.getClass().isArray()) {
            rep = cloneArray(rep);
        }

        if (rep != obj) {
            if (rep != null) {
                if (rep.getClass().isArray()) {
                    this.filterCheck(rep.getClass(), Array.getLength(rep));
                } else {
                    this.filterCheck(rep.getClass(), -1);
                }
            }

            obj = rep;
            this.handles.setObject(this.passHandle, rep);
        }
    }
    ...
}

判断无参构造方法是否存在之后,又调用了 hasReadResolveMethod()方法:

boolean hasReadResolveMethod() {
    this.requireInitialized();
    return this.readResolveMethod != null;
}

逻辑非常简单,就是判断readResolveMethod 是否为空,不为空就返回 true。那么 readResolveMethod是在哪里赋值的呢? 通过全局查找找到了赋值代码在私有方法 ObjectStreamClass()方法中给 readResolveMethod 进行赋值,来看代码:

 ObjectStreamClass.this.readResolveMethod = ObjectStreamClass.getInheritableMethod(cl, "readResolve", (Class[])null, Object.class);

代码的逻辑其实就是通过反射找到一个无参的 readResolve()方法,并且保存下来,现在再回到 ObjectInputStreamreadOrdinaryObject() 方法继续往下看,如果readResolve()存在则调用 invokeReadResolve()方法:

Object invokeReadResolve(Object obj) throws IOException, UnsupportedOperationException {
    this.requireInitialized();
    if (this.readResolveMethod != null) {
        try {
            return this.readResolveMethod.invoke(obj, (Object[])null);
        } catch (InvocationTargetException var4) {
            Throwable th = var4.getTargetException();
            if (th instanceof ObjectStreamException) {
                throw (ObjectStreamException)th;
            } else {
                throwMiscException(th);
                throw new InternalError(th);
            }
        } catch (IllegalAccessException var5) {
            throw new InternalError(var5);
        }
    } else {
        throw new UnsupportedOperationException();
    }
}

我们可以看到在 invokeReadResolve()方法中用反射调用了 readResolveMethod() 方法。 通过JDK源码分析我们可以看出,虽然,增加 readResolve()方法返回实例,解决了单例被破坏的问题。但是,我们通过分析源码以及调试,我们可以看到实际上实例化了两 次,只不过新创建的对象没有被返回而已.

那如果,创建对象的动作发生频率增大,就 意味着内存分配开销也就随之增大;为了解决这个问题,我们推荐使用注册式单例。

为何建议使用注册式(枚举式)单例

我们在前文中说到了,我们极力推荐使用枚举类型的单例;接下来我们分析一下原因:

使用 Java 反编译工具 Jad(自行下载),解压后,使用命令行调用:

./jad ~/IdeaProjects/own/java-advanced/01.DesignPatterns/design-patterns/build/classes/java/main/com/eamon/javadesignpatterns/singleton/enums/EnumSingleton.class

会在当前目录生成一个 EnumSingleton.jad文件,我们使用 vscode 打开这个文件查看:

public final class EnumSingleton ext
首页 上一页 1 2 3 4 5 下一页 尾页 3/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇设计模式-行为型-访问者模式 下一篇通俗易懂设计模式解析——迭代器..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目