【设计模式系列】2.单例模式

目录

一、单例模式的实现方式

1、饿汉式

2、懒汉式

3、双重检测锁式

4、静态内部类

5、集合容器

6、枚举实现

二、单例模式的相关问题

1、反序列化会重新生成对象的问题?

2、使用反射时会强行调用构造器实例化单例类?


一、单例模式的实现方式

概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点,比如:线程池、缓存、对话框、注册表、日志记录等。

1、饿汉式

饿汉式是在jvm加载这个单例类的时候,就会初始化这个类中的实例,在使用单例中的实例时直接拿来使用就好,因为加载这个类的时候就已经完成初始化,并且由于是已经加载好的单例实例因此是线程安全的,并发获取的情况下不会有问题,是一种可投入使用的可靠单例。

// 测试
@Test
void test1(){
    SingleInstance1.getInstance();
}

优点:使用效率高,线程安全。

缺点:由于JVM在加载单例类时需要初始化单例类的实例,对JVM内存不够友好!不推荐使用。

2、懒汉式

这种简单的懒汉式,弥补了饿汉式的缺点,JVM加载单例类时不会去初始化实例了,使用到的时候再去初始化,但在多线程环境中可能会引发线程不安全的问题,最直接的办法是在该方法上添加synchronized关键字,但这样做会锁住整个方法,也是不合理的。

优点:对JVM内存友好,实现了延迟加载;

缺点:不加锁会造成线程不安全问题,加锁又会锁住整个方法,造成性能问题,得不偿失,不推荐。可以尝试使用synchronized锁住代码块(双重检测锁)的方式解决这个问题。

3、双重检测锁式

双重检测锁:当第一次创建单例实例的时候,只有一个线程可以去创建实例,因此不会出现多个线程获取不同实例的情况。

优点:保证了线程安全;

缺点:首次加载稍慢,Java的内存模型在并发过程中具有原子性、可见性、有序性等特点,为了避免“指令重排序”现象和“工作内存与主内存同步延迟”现象,以及一个线程改变的变量值,并不会立刻对其他线程可见等问题,需要加入volatile变量屏蔽指令重排。

4、静态内部类

优点:外部类加载时并不会立即加载内部类,只有当getInstance()方法第一次被调用时,才会导致JVM加载内部类去初始化单例类的实例,这种方式不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。

缺点:由于是静态内部类的形式去创建单例的,故外部无法传递参数进去。

5、集合容器

使用Map集合容器统一维护和管理类的实例,将静态创建的实例添加到集合中,通过key获取对应的单例实例即可,推荐使用。

6、枚举实现

是单例模式中最简单的一种实现方式,推荐使用,例如:

public enum SingleInstanceEnum {
     INSTANCE;
}

二、单例模式的相关问题

1、反序列化会重新生成对象的问题?

描述:除了枚举、容器之外的其他方式会因为反序列化生成了对象,导致单例模式被破坏。

解决方式:在单例类中添加readResolve()方法,用返回的对象替换反序列化时创建的对象即可。

private Object readResolve() throws ObjectStreamException{
    return singleInstance;
}

2、使用反射时会强行调用构造器实例化单例类?

描述:除了枚举、容器之外的其他方式会存在这个问题~

解决方式:修改构造器,让它在创建第二个实例的时候抛异常。

private static volatile boolean flag;
private SingleInstance() {
    if (flag) {
        flag = false;
    } else {
        throw new RuntimeException("当前不是首次创建实例!");
    }
}

考虑到上述问题,以及采用推荐的静态内部类实现,完整的代码参考如下:

/**
 * 静态内部类:当方法被调用时才加载内部类,确保了单例的唯一,实现了懒加载,线程安全
 */
public class SingleInstance4 {

    private static volatile boolean flag;
    // 防止反射强行调用构造器进行单例类的实例化
    private SingleInstance4() {
        if(flag){
            flag = false;
        }else {
            throw new RuntimeException("非首次创建该单例的实例!");
        }
    }

    private static class Inner{
        private static final SingleInstance4 singleInstance = new SingleInstance4();
    }

    public static SingleInstance4 getInstance(){
        return Inner.singleInstance;
    }

    // 防止反序列化生成新的对象来破坏单例
    private Object readResolve()throws ObjectStreamException {
        return Inner.singleInstance;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值