【多线程十六】多线程下的单例模式

单例模式

单例模式有很多实现方法,饿汉、懒汉、静态内部类、枚举类,试分析每种实现下获取单例对象(即调用getInstance)时的线程安全,并思考注释中的问题

饿汉式:类加载就会导致该单实例对象被创建

懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建

实现一
// 问题1:为什么加 final
// 问题2:如果实现了序列化接口, 还要做什么来防止反序列化破坏单例
public final class Singleton implements Serializable {
    // 问题3:为什么设置为私有? 是否能防止反射创建新的实例?
    private Singleton() {}
    // 问题4:这样初始化是否能保证单例对象创建时的线程安全?
    private static final Singleton INSTANCE = new Singleton();
    // 问题5:为什么提供静态方法而不是直接将 INSTANCE 设置为 public, 说出你知道的理由
    public static Singleton getInstance() {
        return INSTANCE;
    }
    public Object readResolve() {
        return INSTANCE;
    }
}

问题1:加了final后可以防止子类继承该单例,破坏了单例模式

问题2:通过readResolve()方法,将单例对象返回,在反序列化时JAVA会调用readResolve,使用该方法返回的对象作为结果

问题3:防止其他类无限创建对象,不能防止反射创建新的实例

问题4:静态成员变量初始化在类加载时实现的,由jvm保证其线程安全性

问题5:用方法可以提供封装性,内部可以改进为懒汉式,还可以支持泛型

实现二
// 问题1:枚举单例是如何限制实例个数的
// 问题2:枚举单例在创建时是否有并发问题
// 问题3:枚举单例能否被反射破坏单例
// 问题4:枚举单例能否被反序列化破坏单例
// 问题5:枚举单例属于懒汉式还是饿汉式
// 问题6:枚举单例如果希望加入一些单例创建时的初始化逻辑该如何做
enum Singleton { 
    INSTANCE; 
}

问题1:枚举定义的对象–》静态成员变量,定义了几个对象就有几个,通过反编译查看知道INSTANCE是枚举类的静态成员变量且是单实例的

问题2:创建时不存在并发问题,静态成员变量,在类加载时完成

问题3:不能

问题4:枚举类默认实现了序列化接口,所以默认可以被序列化&反序列化,且考虑到了反序列化时破坏单例的问题

问题5:饿汉式

问题6:添加一个构造方法,在构造方法中去进行初始化操作

实现三

懒汉式,锁的范围较大,每次调用都需要加锁,影响性能

public final class Singleton {
    private Singleton() { }
    private static Singleton INSTANCE = null;
    // 分析这里的线程安全, 并说明有什么缺点
    public static synchronized Singleton getInstance() {
        if( INSTANCE != null ){
            return INSTANCE;
        } 
        INSTANCE = new Singleton();
        return INSTANCE;
    }
}
实现四
public final class Singleton {
    private Singleton() { }
    
    // 问题1:解释为什么要加 volatile ?
    private static volatile Singleton INSTANCE = null;
    
    // 问题2:对比实现3, 说出这样做的意义 
    public static Singleton getInstance() {
        if (INSTANCE != null) { 
            return INSTANCE;
        }
        synchronized (Singleton.class) { 
            // 问题3:为什么还要在这里加为空判断, 之前不是判断过了吗
            if (INSTANCE != null) { // t2 
                return INSTANCE;
            }
            INSTANCE = new Singleton(); 
            return INSTANCE;
        } 
    }
}

问题1:禁止指令重排–》dcl

问题2:缩小了加锁的范围,提高了性能

问题3:防止首次创建INSTANCE对象时,多线程并发时重复创建单例对象的操作

实现五

采用了静态内部类,只有使用时才会加载且只加载一次,完成懒汉式的创建

public final class Singleton {
    private Singleton() { }
    // 问题1:属于懒汉式还是饿汉式  懒汉式
    private static class LazyHolder {
        static final Singleton INSTANCE = new Singleton();
    }
    // 问题2:在创建时是否有并发问题 
    public static Singleton getInstance() {
        return LazyHolder.INSTANCE;
    }
}

问题2:用到才会初始化,由jvm保证其线程安全性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值