> 一、单例模式 - Yuyy
Yuyy
Yuyy
一、单例模式

解决的问题

  • 有些数据在系统中只应该保存一份,比如系统的配置信息类
  • 资源访问冲突的问题,比如多个logger写入同一个日志文件

几种实现方式

饿汉式

  • 静态成员变量,类加载时实例化

  • 线程安全

  • 不支持延迟加载

    http://bed.yuyy.info/HungryManDemo2.jpg

public class HungryManDemo {

    private static final HungryManDemo instance = new HungryManDemo();

    private HungryManDemo() {}

    public static HungryManDemo getInstance(){
        return instance;
    }
}

懒汉式

  • 支持延迟加载

  • 不支持高并发

    http://bed.yuyy.info/LazyManDemo2.jpg

public class LazyManDemo {

    private static LazyManDemo instance;

    private LazyManDemo() {}

    public static LazyManDemo getInstance() {
        if (instance == null) {
            instance = new LazyManDemo();
        }
        return instance;
    }
}

懒汉式——双重检查

  • 支持高并发

  • 支持延迟加载

  • 使用volatile,禁止指令重排序

  • 在双重检测时可使用局部变量优化,减少访问volatile修饰的变量,以提升性能

  • 实现复杂

    http://bed.yuyy.info/LazyManDoubleCheckDemo2.jpg

public class LazyManDoubleCheckDemo {

    private static volatile LazyManDoubleCheckDemo instance;

    private LazyManDoubleCheckDemo() {}

    public static LazyManDoubleCheckDemo getInstance() {
        LazyManDoubleCheckDemo temp = instance;
        if (temp == null) {
            synchronized (LazyManDoubleCheckDemo.class) {
                temp = instance;
                if (temp == null) {
                    temp = new LazyManDoubleCheckDemo();
                    instance = temp;
                }
            }
        }
        return instance;
    }
}

静态内部类

  • 支持延迟加载

  • 支持高并发

  • 实现比双重检测简单

  • SingletonClass没有static的属性,因此并不会被初始化。直到调用getInstance()的时候,会首先加载SingletonClassInstance类,这个类有一个static的SingletonClass实例,因此需要调用SingletonClass的构造方法,然后getInstance()将把这个内部类的instance返回给使用者。由于这个instance是static的,因此并不会构造多次。

    由于SingletonClassInstance是私有静态内部类,所以不会被其他类知道,同样,static语义也要求不会有多个实例存在。并且,JSL规范定义,类的构造必须是原子性的,非并发的,因此不需要加同步块。同样,由于这个构造是并发的,所以getInstance()也并不需要加同步。

http://bed.yuyy.info/StaticInnerClassDemo2.jpg

public class StaticInnerClassDemo {

    private StaticInnerClassDemo() {}

    private static class SingletonHolder{
        private static final StaticInnerClassDemo instance = new StaticInnerClassDemo();
    }

    public static StaticInnerClassDemo getInstance() {
        return SingletonHolder.instance;
    }
}

枚举

  • 不支持延迟加载

  • 支持高并发

  • 实现最简单

  • 使用枚举除了线程安全和防止反射调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。因此,《Effective Java》作者推荐使用的方法。

http://bed.yuyy.info/EnumDemo.jpg

public enum EnumDemo {
    INSTANCE;
}

序列化问题

反序列化会产生新对象,违反单例规则

解决方案:JVM从内存中反序列化地"组装"一个新对象时,会自动调用类的readResolve方法,我们可以通过此方法返回指定好的对象。
http://bed.yuyy.info/SerialProblem1.jpg

public class SerialProblem {

    private static volatile SerialProblem instance;

    private SerialProblem() {}

    public static SerialProblem getInstance() {
        SerialProblem temp = instance;
        if (temp == null) {
            synchronized (SerialProblem.class) {
                temp = instance;
                if (temp == null) {
                    temp = new SerialProblem();
                    instance = temp;
                }
            }
        }
        return instance;
    }

    private Object readResolve() {
        return instance;
    }
}

反射问题

反射获取构造器,进行实例化产生新对象

解决方案:第二次实例化的时候,抛出异常

http://bed.yuyy.info/ReflectProblem.jpg

public class ReflectProblem {

    private static volatile ReflectProblem instance;

    private ReflectProblem() {
        Assert.isNull(instance);
    }

    public static ReflectProblem getInstance() {
        ReflectProblem temp = instance;
        if (temp == null) {
            synchronized (ReflectProblem.class) {
                temp = instance;
                if (temp == null) {
                    temp = new ReflectProblem();
                    instance = temp;
                }
            }
        }
        return instance;
    }
}

发表评论

textsms
account_circle
email

  • http://1.gravatar.com/avatar/7802fb41e0c9c7690a87c46d99e34da4?s=80&d=monsterid&r=g
    Yuyy博主

    JDK源码里JRELocaleProviderAdapter维护的LocaleProvider全是单例,使用的是DCL,volatile
    image-20210903144157438

    2月前回复

Yuyy

一、单例模式
解决的问题 有些数据在系统中只应该保存一份,比如系统的配置信息类 资源访问冲突的问题,比如多个logger写入同一个日志文件 几种实现方式 饿汉式 静态成员变量,类加载时实例化 …
扫描二维码继续阅读
2021-08-12
友情链接
标签
归档
近期文章
分类
近期文章