美文网首页
单例的工作!

单例的工作!

作者: 我永远喜欢御坂美琴 | 来源:发表于2020-08-05 22:32 被阅读0次

单例模式

单例模式是指在内存中只会创建一次对象的设计模式。在程序中若可以多次使用同一对象且作用相同时则一般选择单例模式,可以避免创建过多相同功能的对象导致内存飙升,达到对象复用,减少内存消耗的效果。

单例模式有两种类型

1.懒汉式:当需要使用对象时再创建。

2.饿汉式:在类加载阶段就进行对象创建。

懒汉式:

对于懒汉式单例,最经典的即是双重检测(double check)。

public class Singleton {
    
    private static volatile Singleton singleton;
    
    private Singleton(){}
    
    public static Singleton getInstance() {
        if (singleton == null) {  
            synchronized(Singleton.class) {
                if (singleton == null) { 
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
    
}

对于双重检测写法,值得注意的就是private static volatile Singleton singleton;中所加的volatile关键字。

volatile关键字在此处起防止指令重排序作用。

我们知道,当对象初始化时须经历三个阶段,分别是

1.分配内存空间

2.初始化对象

3.将引用指向所分配的内存地址

在对象初始化阶段有可能会发生指令重排序,此时2和3执行顺序可能会出现颠倒的情况,即先将引用指向内存空间(此时引用不为空)再初始化对象。此时就有可能会出现线程拿到未初始化完成的单例对象。

举个例子:

A线程经过双重检测之后发现对象引用为空,则开始创建对象。此时,jvm对该对象创建过程进行了指令重排序,执行顺序变为

1.分配内存空间

3.将引用指向所分配的内存地址(此时引用不为空)

2.初始化对象

当A线程执行到第二步:“将引用指向所分配的内存地址”时,singleton引用便不再为空了,此时A线程却仍未对对象进行初始化,即singleton引用指向的是一个未初始化完成的对象。此时B线程进行判空操作,发现singleton引用并不为空,则返回了A线程未初始化完成的对象。

饿汉式:

public class Singleton{
    
    private static final Singleton singleton = new Singleton();
    
    private Singleton(){}
    
    public static Singleton getInstance() {
        return singleton;
    }
}

破坏单例:

利用反射和序列化反序列化操作即可破坏以上两种单例模式。

反射:

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取构造器
        Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
        //访问私有构造方法
        constructor.setAccessible(true);
        Singleton singleton = constructor.newInstance();
        System.out.println(singleton == Singleton.getInstance());//返回false
    }

序列化反序列化:

public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("d://Singleton"));
        objectOutputStream.writeObject(Singleton.getInstance());
        File file = new File("d://Singleton");
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
        Singleton newinstance = (Singleton) objectInputStream.readObject();
        System.out.println(Singleton.getInstance() == newinstance);
    }

两个对象地址不相等的原因是:readObject() 方法读入对象时它必定会返回一个新的对象实例,必然指向新的内存地址。

避免被破坏:

《Effective Java》一书中提到一个解决方法,可以避免被破坏。

public enum Singleton {
    INSTANCE;
}

即是使用枚举类型,枚举类型具有天然的线程安全性和单一实例性质,当枚举类被加载时便会调用其空参构造器构造INSTANCE对象,之后便不会再创建(属于饿汉式)。枚举类型最亮眼的是防止反射和序列化反序列化操作破坏单例模式。

1.反射:

在反射构造对象过程中会判断该类是否为枚举类型,若是,则抛出异常,终止创建。

2.序列化反序列化

枚举类型在进行序列化时只是写入枚举类类型和名字,反序列化时进行读出并通过名字使用valueOf方法获取对应实例。


私货:

相关文章

  • 单例的工作!

    单例模式 单例模式是指在内存中只会创建一次对象的设计模式。在程序中若可以多次使用同一对象且作用相同时则一般选择单例...

  • Swift设计模式(0) - 单例模式

    简单介绍单例模式 单例模式其实大家应该都耳熟能详了,至少在工作上会时不时的听到单例这个词。那么什么是单例模式,简单...

  • 如何对枚举类型实现的单例模式进行mock

    最近在工作中使用了用枚举实现的单例,那么如何对该种单例模式实现mock呢? 单例类如下(Singleton.cla...

  • 详解设计模式之单例模式

    1、�什么是单例? 单例顾名思义就是单个实例。日常工作都是自由的通过构造方法创建对象的,所以听到单例时,我们应该意...

  • 2020-11-02-Spring单例 vs. 单例模式

    Spring 单例不是 Java 单例。本文讨论 Spring 的单例与单例模式的区别。 前言 单例是 Sprin...

  • IOS单例模式的底层原理

    单例介绍 本文源码下载地址 1.什么是单例 说到单例首先要提到单例模式,因为单例模式是单例存在的目的 单例模式是一...

  • Android设计模式总结

    单例模式:饿汉单例模式://饿汉单例模式 懒汉单例模式: Double CheckLock(DCL)实现单例 Bu...

  • IOS学习笔记之单例

    单例介绍 1.什么是单例 说到单例首先要提到单例模式,因为单例模式是单例存在的目的 单例模式是一种常用的软件设计模...

  • OC - 单例模式

    导读: 一、什么是单例模式 二、单例的作用 三、常见的单例类 四、自定义单例类的方法 一、什么是单例模式 单例模式...

  • 【设计模式】单例模式

    单例模式 常用单例模式: 懒汉单例模式: 静态内部类单例模式: Android Application 中使用单例模式:

网友评论

      本文标题:单例的工作!

      本文链接:https://www.haomeiwen.com/subject/ditjrktx.html