head first 设计模式笔记4-单例模式

时间:2019-09-03
本文章向大家介绍head first 设计模式笔记4-单例模式,主要包括head first 设计模式笔记4-单例模式使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

目录

  1、单例模式(Singleton Pattern)
  2、概念
  3、饿汉式:不是延迟加载,加载类的时候直接初始化
  4、懒汉式:延迟加载,首次需要使用的时候在实例化,需要考虑线程安全
  5、静态内部类实现
  6、枚举实现

1、单例模式(Singleton Pattern)   <==返回目录

    确保一个类只有一个实例,并提供一个全局访问点。

2、概念   <==返回目录

下面的内容转载自博客: 23种设计模式之单例模式 ( https://chenmingyu.top/design-singleton/)

  单例模式:单例模式属于创建型模式;

  定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式目的是保证在程序运行期间一个类只有一个实例,并提供一个全局访问点,无论什么情况下,只会生成一个实例,免去繁琐的创建销毁对象的过程。

  优点:

1)减少了内存开支,避免频繁地创建、销毁对象;
2)避免对资源的多重占用;

  缺点:

没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化

  如何设计单例模式

    如何设计单例模式其实很简单,只需要考虑一个问题,实例是否可以保证是全局唯一。关于实例是否保证是全局唯一延伸出的问题:

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

 

  所以设计一个安全的单例需要考虑的问题还是很多的。针对上述问题常见的解决办法:

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

  

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

  常见的单例模式实现

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

  分个类:

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

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

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

  

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

3、饿汉式:不是延迟加载,加载类的时候直接初始化   <==返回目录

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

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

/**
 * 单例模式:饿汉式
 * @author oy
 * @date 2019年9月3日 下午11:13:49
 */
public class Singleton {

    private static Singleton singleton = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance(){
        return singleton;
    }
}

4、懒汉式:延迟加载,首次需要使用的时候在实例化,需要考虑线程安全   <==返回目录

  线程不安全的实现方式:

/**
 * 单例模式:懒汉式,线程不安全
 * @author oy
 * @date 2019年9月3日 下午11:15:47*/
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)

/**
 * 单例模式:双重检查(DCL:Double Check Lock)
 * @author oy
 * @date 2019年9月3日 下午11:17:06
 */
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重排序优化,所以就不会出现上面的问题

5、静态内部类实现   <==返回目录

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

/**
 * 单例模式:静态内部类实现
 * @author oy
 * @date 2019年9月3日 下午11:17:06
 */
public class Singleton {

    private Singleton() {}

    public static Singleton getInstance() {
        return SingletonHolder.SINGLETON;
    }

    private static class SingletonHolder {
        private static final Singleton SINGLETON = new Singleton();
    }
}

6、枚举实现   <==返回目录

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

/**
 * 单例模式:枚举实现
 * @author oy
 * @date 2019年9月3日 下午11:17:06
 */
public enum Singleton {

    SINGLETON;

    /**
     * 提供的方法
     */
    public void method() {
        System.out.println("枚举实现");
    }
}

原文地址:https://www.cnblogs.com/xy-ouyang/p/11456448.html