5分钟学会java设计模式之单例模式

                                                 java设计模式之单例模式

一、概述

   1、概念:
        是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点;隐藏其所有的构造方法;属于创建型模式
 
   2、使用场景:
        确保任何情况下都绝对只有一个实例(ServletContext、ServletConfig、ApplicationContext、DBPool)
 
   3、优点:
        a.在内存中只有一个实例,减少了内存开销
        b.可以避免对资源多重占用
        c.设置全局访问点,严格控制访问
 
   4、缺点:
        a.没有接口,扩展困难
        b.如果要扩展单例对象,只有修改代码,没有其他途径

 二、实现方式

       1、饿汉式


/**
 * 描述:饿汉式单例 - 静态属性
 * @author yys
 */
public class HungrySingleton {

    // 私有构造,防止外界new对象
    private HungrySingleton() {}

    private static final HungrySingleton hungrySingleton = new HungrySingleton();

    public static HungrySingleton getHungrySingleton() {
        return hungrySingleton;
    }

}


/**
 * 描述:饿汉式单例 - 静态代码块
 * @author yys
 */
public class HungryStaticSingleton {

    // 私有构造,防止外界new对象
    private HungryStaticSingleton() {}

    private static final HungryStaticSingleton hungryStaticSingleton;

    // 静态代码块赋值
    static {
        hungryStaticSingleton = new HungryStaticSingleton();
    }

    public static HungryStaticSingleton getHungryStaticSingleton() {
        return hungryStaticSingleton;
    }

    // 普及下加载顺序
    // 先静态、后动态 / 先属性、后方法 / 自上而下

}


//  总结:饿汉式单例是在类加载的时就立即初始化,并且创建单例对象。绝对线程安全,在线程还没出现以前就完成实例化,不存在访问安全问题。

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

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

       2、懒汉式


/**
 * 描述:懒汉式单例 - 同步方法
 *     优点:线程安全
 *     缺点:(性能低) 因本质为同步方法,再线程多的情况下,CPU压力上升,导致大批线程阻塞,导致性能下降
 * @author yys
 */
public class LazySimpleSingleton {

    // 私有构造,防止外界new对象
    private LazySimpleSingleton() {}

    private static LazySimpleSingleton lasy;

    // 同步方法,多线程环境下,线程安全,but:线程多的情况下,CPU压力上升,导致大批线程阻塞,导致性能下降
    public synchronized static LazySimpleSingleton getInstance() {
        if(null == lasy) {
            lasy = new LazySimpleSingleton();
        }
        return lasy;
    }
    
}


/**
 * 描述:懒汉式单例 - 双重检查锁
 *     优点:线程安全,(性能较高) 因本质为同步代码块,较同步方法的方式来说,性能大幅度提升
 *     缺点:用到synchronized关键字,性能还是存在影响。
 * @author yys
 */
public class LazyDoubleCheckSingleton {

    // 私有构造,防止外界new对象
    private LazyDoubleCheckSingleton() {}

    // volatile保证可见性和禁止指令重排序
    private volatile static LazyDoubleCheckSingleton lasy;

    public static LazyDoubleCheckSingleton getInstance() {
        // 第一重检查锁
        if(null == lasy) {
            synchronized (LazyDoubleCheckSingleton.class) {
                // 第二重检查锁
                if(null == lasy) {
                    lasy = new LazyDoubleCheckSingleton();
                }
            }
        }
        return lasy;
    }

    // 创建对象的顺序:
    // 1、分配对象的内存空间
    // 2、初始化对象
    // 3、设置lasy指向刚分配的内存地址

    // 为什么需要volatile:防止JVM指令重排序

}


/**
 * 描述:懒汉式单例 - 静态内部类
 *     优点:线程安全,兼顾饿汉式的内存浪费,也兼顾synchronized锁的性能问题
 *     缺点:通过反射机制可以破坏单例
 *
 *     反射机制破坏单例解决方案:在构造方法中做一些限制,一旦出现多次重复创建,则直接抛出异常
 *
 * @author yys
 */
public class LazyInnerClassSingleton {

    // 私有构造,防止外界new对象
    private LazyInnerClassSingleton() {
        // 防止反射机制破坏单例
        if(null != LazyHolder.LAZY) {
            throw new RuntimeException("不允许通过反射机制创建多个实例");
        }
    }

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

    // 默认不加载
    private static class LazyHolder {
        // 这里其实是饿汉式单例
        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
    }

}


//  总结:被外部类调用时才会加载,创建实例。

       3、登记式单例


/**
 * 描述:登记式单例
 *          枚举式单例
 * @author yys
 */
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;
    }

}


/**
 * 描述:登记式单例
 *          容器式单例
 * @author yys
 */
public class ContainerSingleton {

    // 私有构造,防止外界new对象
    private ContainerSingleton() {}

    private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>(0);

    private static Object getBean(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);
            }
        }
    }

    /**
     * 记录: spring ioc 容器位置
     *      org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
     *
     *      159行
     *      private final ConcurrentMap<String, BeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>();
     */

}


//  总结:将每一个实例都缓存到统一的容器中,使用唯一标识获取实例

扫描二维码关注公众号,回复: 10813361 查看本文章

       4、ThreadLocal 线程单例  


/**
 * 描述:ThreadLocal 线程单例
 * @author yys
 */
public class ThreadLocalSingleton {

    // 私有构造,防止外界new对象
    private ThreadLocalSingleton() {}

    // ThreadLocal 不能保证其创建的对象是全局唯一,但是能保证在单个线程中是唯一的,天生的线程安全
    private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
            new ThreadLocal<ThreadLocalSingleton>() {
                @Override
                protected ThreadLocalSingleton initialValue() {
                    return new ThreadLocalSingleton();
                }
            };

    public static ThreadLocalSingleton getInstance() {
        return threadLocalInstance.get();
    }

}

//  总结:不能保证其创建的对象是全局唯一,保证线程内部的全局唯一,且天生线程安全

                       Now ~ ~ ~写到这里,就写完了,如果有幸帮助到你,请记得关注我,共同一起见证我们的成长

发布了74 篇原创文章 · 获赞 253 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/qq_42175986/article/details/97105544