单例模式详细学习

1.对单例模式的理解

核心: 构造方法私有化,保证在整个项目中该类的实例最多只有一个

分类:

image.png

饿汉式与懒汉式的区别:

饿汉式都是在类初始化就创建对象,该阶段的线程安全交由JVM来保证,我们不用考虑线程安全问题。而懒汉式需要我们人为思考如何解决线程安全问题。

2.写出5种单例模式的具体实现

普通饿汉式
/**
 * 饿汉式  只要类初始化该实例就已被创建,提前创建
 */
public class Singleton1 implements Serializable {
    private Singleton1(){
        //防止通过反射技术来破环单例
        if(INSTANCE != null){
            throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("private Singleton1()");
    }
    private static final Singleton1 INSTANCE = new Singleton1();
    public static  Singleton1 getInstance(){
        return INSTANCE;
    }
    //防止反序列化破环单例
    public Object readResolve(){
        return INSTANCE;
    }
}
枚举饿汉式
/**
 * 枚举饿汉式,只要类初始化该枚举对象就已被创建,提前创建
 */
public enum Singleton2{
    INSTANCE;

    //枚举类的构造方法默认修饰符就是private
    Singleton2(){
        System.out.println("private Singleton2");
    }
    public static Singleton2 getInstance(){return INSTANCE;}

}
普通懒汉式
/**
 * 懒汉式,要注意线程安全问题破环单例
 */
public class Singleton3 implements Serializable {
    private Singleton3(){
        System.out.println("private Singleton3()");
    }

    private static Singleton3 INSTANCE = null;
    //加入synchronized防止多线程安全问题,虽能解决,但严重影响性能
    public static synchronized Singleton3 getInstance(){
        if(INSTANCE == null){
            INSTANCE = new Singleton3();
        }
        return INSTANCE;
    }
    //防止反序列化破环单例
    public Object readResolve(){
        return INSTANCE;
    }
}
DCL懒汉式
/**
 * 懒汉式 DCL(Double Checked Lock),双检锁解决线程安全
 */
public class Singleton4 implements Serializable {
    private Singleton4(){
        System.out.println("private Singleton4()");
    }
    private static volatile Singleton4 INSTANCE = null;//可见性,有序性,加入volatile防止指令重排
    //加锁之前先判断,只有当INSTANCE还未创建阶段才会加锁,一旦INSTANCE创建完毕,则直接返回,提高了性能
    public static  Singleton4 getInstance(){
        if(INSTANCE == null){
            synchronized (Singleton4.class){
                if(INSTANCE == null){
                    INSTANCE = new Singleton4();
                }
            }
        }
        return INSTANCE;
    }
    //防止反序列化破环单例
    public Object readResolve(){
        return INSTANCE;
    }
}
为什么用 volatile?

这是因为new关键字创建对象不是原子操作,会经历以下三步:

  1. 在堆内存开辟内存空间
  2. 调用构造方法,初始化对象
  3. 引用变量指向堆内存空间

操作系统有权对指令的执行顺序进行优化,改变在逻辑上没有关联的指令的执行顺序。这是1 2 3三个步骤就有可能变为 1 3 2,那么就有可能出线程1刚执行完1 3 步,还未给对象初始化,线程2执行到了INSTACE == nullif判断,结果未false,直接返回了还未初始化的对象。而一旦加了volatile关键字,就会禁止对INSTANCE赋值过程时的指令重排列。

静态内部类懒汉式
/**
 * 懒汉式 - 静态内部类实现(比较推荐的懒汉式实现方式)
 * 由于静态成员变量的赋值会放在静态代码块执行,而静态代码块会由JVM来保证线程安全问题,它里面的代码只会被执行一次
 */
public class Singleton5 {
    private Singleton5() {
        System.out.println("private Singleton5");
    }
    private static class Holder {
        static Singleton5 INSTANCE = new Singleton5();
    }
    public static Singleton5 getInstance() {
        return Holder.INSTANCE;
    }
}

3. 如何破环单例?

3.1. 反射(对枚举饿汉式无效)
public static void reflection(Class<?> clazz) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    Constructor<?> constructor = clazz.getDeclaredConstructor();
    constructor.setAccessible(true);  //修改访问权限
    System.out.println("反射获得实例:"+constructor.newInstance());
}
3.2 反序列化(看该类是否有readResolve()方法)
public static void serializable(Object instance) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(instance);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        //该新对象的创建不走构造方法
        //然而要注意当执行readObject方法时,会去原类里寻找readResolve方法,如果找到走该方法逻辑,如果该方法不存在则直接创建新对象
        System.out.println("反序列化创建实例:"+ ois.readObject());
    }
3.3 Unsafe类(无预防措施)
 public static void unsafe(Class<?> clazz) throws InstantiationException {
        //这里需要借助Spring提供的UnsafeUtils拿到Unsafe对象,该方法同样不走构造方法,无法避免
        Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);
        System.out.println("Unsafe创建实例:"+ o);
    }

4. 哪里用到了单例模式?

1. JDK
  • Runtime
  • Console
2. Windows
  • Task Manager(任务管理器)
  • Recycle Bin(回收站)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值