zoukankan      html  css  js  c++  java
  • 23种设计模式之单例模式

    单例模式属于创建型模式,保证在程序运行期间一个类只有一个实例,并提供一个全局访问点

    推荐访问我的个人网站,排版更好看呦:
    https://chenmingyu.top/design-singleton/

    什么是单例模式

    单例模式目的是保证在程序运行期间一个类只有一个实例,并提供一个全局访问点,无论什么情况下,只会生成一个实例,免去繁琐的创建销毁对象的过程。

    如何设计单例

    如何设计单例模式其实很简单,只需要考虑一个问题,实例是否可以保证是全局唯一,只要满足这个条件,这个单例设计的肯定就合格了。

    关于实例是否可以保证是全局唯一的延伸出的问题:

    1. 是否线程安全,不安全肯定就不能保证全局只有一个实例
    2. 是否支持序列化,支持序列化的类,被反序列化之后肯定就不是全局唯一了
    3. 是否支持反射,支持反射肯定也不是全局唯一的
    4. 是否可以被克隆,这个也不能保证全局唯一

    所以设计一个安全的单例需要考虑的问题还是很多的。

    针对上述问题常见的解决办法:

    1. 保证线程安全,使用volatile+synchronized实现
    2. 防止序列化攻击,重写readResolve方法
    3. 防止反射,常用的方案是在单例类里增加一个boolean类型的flag标识,在实例化的时候先判断flag标识
    4. 防止克隆,重写clone()方法

    实现一个最简单的单例就需要考虑到以上的所有问题,这个时候什么有用的方法还没写那,代码就已经很多了,那有没有简单的办法既满足上述条件,代码又简洁那,那肯定有,使用枚举实现单例。

    常见的单例模式设计方案

    常见的单例模式设计方案大概有五种,懒汉模式,饿汉模式,双重检查方式实现,静态内部类实现,枚举实现。

    简单的分个类:

    1. 是否支持延迟加载,分为懒汉模式和饿汉模式
    2. 线程安全设计了双重检查模式实现,静态内部类实现方式
    3. 不支持序列化,反射,克隆,枚举实现方式

    其中懒汉模式,饿汉模式,双重检查方式实现,静态内部类的实现方式都可以概括为以下两步:

    1. 构造函数私有化,保证在外部无法new对象
    2. 提供一个static方法获取当前实例(不同方案,实现不同)

    当然枚举的实现方式最简单,也最安全的,所以推荐使用枚举实现,其次推荐使用静态内部类方式实现。

    饿汉模式

    不是延迟加载,加载类的时候直接初始化

    /**
     * @auther: chenmingyu
     * @date: 2019/2/12 16:26
     * @description:
     */
    public class Singleton {
    
        private static Singleton singleton = new Singleton();
    
        private Singleton() {
        }
    
        public static Singleton getInstance(){
            return singleton;
        }
    }
    

    优点:线程安全,代码简单。

    缺点:不是延迟加载,如果你用不到这个类,它也会实例化,还有一个问题就是如果这个实例依赖外部一些配置文件,参数什么的,在实例化之前就要获取到,否则就实例化异常

    懒汉模式

    延迟加载,首次需要使用的时候在实例化,需要考虑线程安全

    线程不安全的实现方式

    public class Singleton {
    
        private static Singleton singleton;
    
        private Singleton() {
        }
    
        public static Singleton getInstance(){
            if(null == singleton){
                singleton = new Singleton();
            }
            return singleton;
        }
    }
    
    双重检查(DCL:Double Check Lock)

    线程安全的实现方式:

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

    面试官:为什么使用volatile修饰singleton变量?

    1. 说的volatile,首先肯定回答volatile的可见性
    2. 防止重排序优化,如果不用volatile修饰,多线程的情况下,可能会出现线程A进入synchronized代码块,执行new Singleton();,首先给singleton分配内存,但是还没有初始化变量,这时候线程B进入getInstance方法,进行第一个判断,此时singleton已经不为空,直接返回singleton,然后肯定报错。使用volatile修饰之后禁止jvm重排序优化,所以就不会出现上面的问题
    静态内部类实现

    使用静态内部类实现也是延迟加载,利用静态内部类去实现线程安全,只有在第一次调用getInstance方法的时候才会去加载SingletonHolder,初始化SINGLETON

    public class Singleton {
    
        private Singleton() {
        }
    
        public static Singleton getInstance(){
            return SingletonHolder.SINGLETON;
        }
    
        private static class SingletonHolder{
            private static final Singleton SINGLETON = new Singleton();
        }
    }
    
    枚举实现

    枚举实现代码更简洁,线程安全,并且保证枚举不会被反序列化,反射和克隆

    /**
     * @auther: chenmingyu
     * @date: 2019/2/12 16:30
     * @description:
     */
    public enum Singleton {
        
        SINGLETON;
    
        /**
         * 提供的方法
         */
        public void method(){
            System.out.println("枚举实现");
        }
    }
    

    所以推荐使用枚举方式,调用Singleton.SINGLETON.method();

  • 相关阅读:
    第二周学习进度总结
    《大道至简》阅读收获及个人总结
    代码层面解释安全性
    2020年大三下学期第二周学习心得
    信息化领域热词分类分析及解释实战
    2020年新型冠状病毒疫情分析实战
    《架构漫谈》个人理解概括
    北京市信件内容爬虫实例——首都之窗
    2020寒假生活学习日记(十五)
    2020寒假生活学习日记(十四)
  • 原文地址:https://www.cnblogs.com/cmyxn/p/10371504.html
Copyright © 2011-2022 走看看