zoukankan      html  css  js  c++  java
  • 单例模式

    一、饿汉式单例

    饿汉式单例是在类加载的时候就立即初始化,并且创建单例对象。绝对线程安全,在线
    程还没出现以前就是实例化了,不可能存在访问安全问题。
    优点:没有加任何的锁、执行效率比较高,在用户体验上来说,比懒汉式更好。
    缺点:类加载的时候就初始化,不管用与不用都占着空间,浪费了内存,有可能占着茅
    坑不拉屎。

    Spring中IOC容器ApplicationContext本身就是典型的饿汉式单例。接下来看一段代
    码:

    //饿汉式单例
    // 它是在类加载的时候就立即初始化,并且创建单例对象

    //优点:没有加任何的锁、执行效率比较高,
    //在用户体验上来说,比懒汉式更好

    //缺点:类加载的时候就初始化,不管你用还是不用,我都占着空间
    //浪费了内存,有可能占着茅坑不拉屎

    //绝对线程安全,在线程还没出现以前就是实例化了,不可能存在访问安全问题
    public class HungrySingleton {
    //先静态、后动态
    //先属性、后方法
    //先上后下
    private static final HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton(){}

    public static HungrySingleton getInstance(){
    return hungrySingleton;
    }
    还有另外一种写法,利用静态代码块的机制:
    //饿汉式静态块单例
    public class HungryStaticSingleton {
    private static final HungryStaticSingleton hungrySingleton;
    static {
    hungrySingleton = new HungryStaticSingleton();
    }
    private HungryStaticSingleton(){}
    public static HungryStaticSingleton getInstance(){
    return hungrySingleton;
    }
    }

    二、懒汉式单例
    懒汉式单例的特点是:被外部类调用的时候内部类才会加载,下面看懒汉式单例的简单
    实现LazySimpleSingleton:
    //懒汉式单例
    //在外部需要使用的时候才进行实例化 线程不安全
    public class LazySimpleSingleton {
    private LazySimpleSingleton(){}
    //静态块,公共内存区域
    private static LazySimpleSingleton lazy = null;
    public static LazySimpleSingleton getInstance(){
    if(lazy == null){
    lazy = new LazySimpleSingleton();
    }
    return lazy;
    }
    }

    实现线程安全的单例:
    //懒汉式单例
    //在外部需要使用的时候才进行实例化
    public class LazySimpleSingleton {
    private LazySimpleSingleton(){}
    //静态块,公共内存区域
    private static LazySimpleSingleton lazy = null;
    //加锁,保证线程安全,但是锁住的粒度太大,导致性能低但是,用
    //synchronized加锁,在线程数量比较多情况下,如果CPU 分配压力上升,会导致大批
    //量线程出现阻塞,从而导致程序运行性能大幅下降。
    public synchronized static LazySimpleSingleton getInstance(){
    if(lazy == null){
    lazy = new LazySimpleSingleton();
    }
    return lazy;
    }
    }
    双重校验方式实现单例:
    public class LazyDoubleCheckSingleton {
    //volatile是为了让该变量有可见性以及禁止重排序
    private volatile static LazyDoubleCheckSingleton lazy = null;

    private LazyDoubleCheckSingleton(){}
    public static LazyDoubleCheckSingleton getInstance(){
    if(lazy == null){
    synchronized (LazyDoubleCheckSingleton.class){
    if(lazy == null){
    lazy = new LazyDoubleCheckSingleton();//第7行
    //1.分配内存给这个对象
    //2.初始化对象
    //3.设置lazy指向刚分配的内存地址
    //4.初次访问对象
    }
    }
    }
    return lazy;
    }
    }
    双重校验锁为什么要加volatile关键字修饰?
     lazy = new LazyDoubleCheckSingleton();//第7行这个创建了一个对象,这一行代码可以分解为
    如下的3行伪代码。
    memory=allocate();//1.分配对象的内存空间
    ctorInstance(memory);//2初始化对象
    instance=memory;//3.设置instance指向刚分配的内存地址
    上面3行伪代码中的2和3之间,可能会被重排序,2和3之间重排序之后的执行时序如下:
    1 3 2
    由于可能存在指令重排序,即线程A执行了第3步,没执行第2步,即对象并未初始化完成,导致线程B在判断对象是否为空时,获取到了一个未完全初始化完成的对象,所以要通过volatile来禁止指令重排序;
     
    
    
    //这种形式兼顾饿汉式的内存浪费,也兼顾synchronized性能问题
    //完美地屏蔽了这两个缺点
    //史上最牛B的单例模式的实现方式
    public class LazyInnerClassSingleton {
    //默认使用LazyInnerClassGeneral的时候,会先初始化内部类
    //如果没使用的话,内部类是不加载的
    private LazyInnerClassSingleton(){
    if(LazyHolder.LAZY != null){
    throw new RuntimeException("不允许创建多个实例");
    }
    }

    //每一个关键字都不是多余的
    //static 是为了使单例的空间共享
    //保证这个方法不会被重写,重载
    public static final LazyInnerClassSingleton getInstance(){
    //在返回结果以前,一定会先加载内部类
    return LazyHolder.LAZY;
    }

    //默认不加载
    private static class LazyHolder{
    private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
    }
    }

    三:容器式单例
    //Spring中的做法,就是用这种注册式单例
    public class ContainerSingleton {
    private ContainerSingleton(){}
    private static Map<String,Object> ioc = new ConcurrentHashMap<String,Object>();
    public static Object getInstance(String className){
    synchronized (ioc) {
    if (!ioc.containsKey(className)) {
    Object obj = null;
    try {
    obj = Class.forName(className).newInstance();
    ioc.put(className, obj);
    } catch (Exception e) {
    e.printStackTrace();
    }
    return obj;
    } else {
    return ioc.get(className);
    }
    }
    }
    }

    容器式写法适用于创建实例非常多的情况,便于管理。但是,是非线程安全的。到此,
    注册式单例介绍完毕。

    四、枚举单例
    //常量中去使用,常量不就是用来大家都能够共用吗?
    //通常在通用API中使用
    public enum EnumSingleton {
    INSTANCE;
    private Object data;
    public Object getData() {
    return data;
    }
    public void setData(Object data) {
    this.data = data;
    }
    public static EnumSingleton getInstance(){
    return INSTANCE;
    }
    }
     


  • 相关阅读:
    2020软件工程作业04
    2020软件工程作业02
    第一周作业
    2019春总结作业
    12周作业
    第十一周作业
    第十周阅读
    第9周编程总结
    编程总结模版
    第8周编程总结
  • 原文地址:https://www.cnblogs.com/zpp1234/p/13184272.html
Copyright © 2011-2022 走看看