单例的7种实现方式和破坏单例的序列化和反射问题及解决方法

单例的7种实现方式和破坏单例的序列化和反射问题及解决方法

七种单例模式

是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的一个类只有一个实例。即一个类只有一个对象实例。

一、 饿汉式(使用全局的静态常量实现,线程安全)

这种方式基于classloder机制避免了多线程的同步问题。instance在类加载的时候就实例化了,所以不是懒加载。

二、 饿汉式(使用静态代码块实现,线程安全)

这种方式也是基于classloder机制避免了多线程的同步问题。instance在类加载的时候就实例化了,所以不是懒加载。

三、 懒汉式(内部方法实现,非线程安全)

四、 懒汉式(同步方法实现,线程安全)

五、懒汉式(同步代码块实现,线程安全)

六、双重检查(懒加载+双非空判断,线程安全)

七 静态内部类(线程安全)

这种方式也是基于classloder机制避免了多线程的同步问题。至于这个单例是否是懒加载。如果我们知道一个类的静态内部类何时加载的,就会知道。 一个类加载了,它的静态内部类不会被加载,除非我们调用这个类的变量或者方法,这个类才会加载。

? 所以instance实例不会因为Singleton类的加载而加载,我们只有调用getInstance()方法的时候才会去加载SingletonHolder类,从而实例化instance

八、 枚举(线程安全)

这种方式是《Effective Java》作者推荐的方式,他能够避免多线程同步问题。

枚举可解决线程安全问题

定义枚举时使用enum和class一样,是Java中的一个关键字。就像class对应用一个Class类一样,enum也对应有一个Enum类。

通过将定义好的枚举反编译,我们就能发现,其实枚举在经过的编译之后,会被转换成形如的定义。

而且,枚举中的各个枚举项同事通过来定义的。如:

反编译后代码为:

类型的属性会在类被加载之后被初始化,当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的(因为虚拟机在加载枚举的类的时候,会使用ClassLoader的loadClass方法,而这个方法使用同步代码块保证了线程安全)。所以,创建一个enum类型是线程安全的。

也就是说,我们定义的一个枚举,在第一次被真正用到的时候,会被虚拟机加载并初始化,而这个初始化过程是线程安全的。而我们知道,解决单例的并发问题,主要解决的就是初始化过程中的线程安全问题。

所以,由于枚举的以上特性,枚举实现的单例是天生线程安全的。他是随着类的加载而加载的,所以它不是懒加载。

破坏单例模式的反序列化和反射

我们创建完成的单例模式,其实并不是那么完美。 上面列举的7中单例模式,除了 外,其他的6中都有可能出现,使用反序列化和反射,从而破坏单例模式,使我们的单例不在单例了。

反序列化破坏单例问题

以 使用全局的静态常量实现的饿汉式为例,看看反序列化破坏单例的情况。

如果测试反序列化问题,那么我们的类必须实现标记接口Serializable,只有实现Serializable接口的类的实例,才能进行序列化写入文件,才能造成反序列化问题。

进行测试:

    @Test    public void testSynchronized() throws IOException, ClassNotFoundException {Singleton singleton = Singleton.getInsta

来源:起止洺

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2019年7月20日
下一篇 2019年7月20日

相关推荐