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

    单例模式

    概述

    单例模式:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

    分类

    1 .最简单的形式--懒汉写法(线程不安全)

    package Create.SingletonPattern;
    
    /**
     * 懒汉式
     */
    public class LazySingleton {
    
        private static LazySingleton singleton;
    
        private LazySingleton() {
        }
    
        public static LazySingleton getInstance() {
            if (singleton == null) {
                singleton = new LazySingleton();
            }
            return singleton;
        }
    
    }
    

    2 . 懒汉式写法(线程安全)

    package Create.SingletonPattern;
    
    public class LazySingletonEnhence {
    
        private static LazySingletonEnhence singleton;
    
        private LazySingletonEnhence(){}
    
        //    改进版 getInstance方法上加同步
        public static synchronized LazySingletonEnhence getInstance() {
            if (singleton == null) {
                singleton = new LazySingletonEnhence();
            }
            return singleton;
        }
    }
    
    

    优点:初始化才创建
    缺点:每次都需要检查同步,消耗资源

    3. 饿汉式写法

    package Create.SingletonPattern;
    
    /**
     * 饿汉模式
     */
    public class StaffSingleton {
    
        private static final StaffSingleton single = new StaffSingleton();
    
        private StaffSingleton() {
        }
    
        public static StaffSingleton getSingle() {
            return single;
        }
    }
    
    

    饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

    饿汉式和懒汉式区别

    • 饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,
    • 而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。
    • 另外从以下两点再区分以下这两种方式:
      • 线程安全:
        • 饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,
        • 懒汉式本身是非线程安全的。
      • 资源加载和性能:
        • 饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,
        • 而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

    4. 静态内部类

    package Create.SingletonPattern;
    
    class InternalSingleton {
        private static class SingletonHolder {
            private final static InternalSingleton INSTANCE = new InternalSingleton();
        }
    
        private InternalSingleton() {
        }
    
        public static InternalSingleton getInstance() {
            return SingletonHolder.INSTANCE;
        }
    }
    
    

    优点:调用时才初始化静态变量INSTANCE,,也有效避免了DCL失效 ,所以推荐使用这种方式。

    5. 枚举

    package Create.SingletonPattern;
    
    enum  EnumSingleton {
        INSTANCE;
        public void doSomeThing(){
            System.out.println("do some thing");
        }
    }
    
    
    • 这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。默认的枚举实例的创建是线程安全的,但是实例内的各种方法则需要程序员来保证线程安全
    • 但是由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏。
    • 总的来说好处有
      1. 实例的创建线程安全,确保单例
        2. 防止被反射创建多个实例。
        3. 没有序列化的问题。
    • 对于反序列化可以通过复写钩子函数:readResolve(),返回单例对象,默认会重新生成一个新的对象。

    6. 双重校验锁

    package Create.SingletonPattern;
    
    public class LockSingleton{
        private volatile static LockSingleton singleton;
        private LockSingleton(){}
    
        public static LockSingleton getInstance(){
            if(singleton==null){
                synchronized(LockSingleton.class){
                    if(singleton==null){
                        singleton=new LockSingleton(); //非原子性操作
                    }
                }
            }
            return singleton;
        }
    
    }
    
    • singleton=new LockSingleton();是非原子性操作,分为3个步骤
      1. 给LockSingleton实例分配内存
      2. 调用构造函数;初始化成员字段
      3. 将singleton对象指向非配的内存空间
    • 其中 2 和 3 顺序是未定的,容易引起DCL失效,需要借助volatile关键字解决这个问题

    优点:初始化才实例化,效率高
    缺点:第一次翻译稍慢在

    7. 登记式单例

    另类的单例实现,将单例类型注入到一个统一的管理类中,再根据key获取对象对应类型的对象。

    package Create.SingletonPattern;
    
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 可以忽略
     */
    public class SingletonManager {
        private SingletonManager() {
        }
    
        private static Map<String, Object> objectMap = new HashMap<>();
    
        public static void registerService(String key,Object instance){
            if (!objectMap.containsKey(key)){
                objectMap.put(key,instance);
            }
        }
    
        public static Object getService(String key){
            return objectMap.get(key);
        }
    
    }
    

    see source code

  • 相关阅读:
    apollo-springboot 整合使用
    axon 过滤存储哪些事件
    springtools 解决lombok
    mylog 自定义注解打印 logger
    java高级应用:线程池全面解析
    监控 Java 线程池运行状态
    线程池获当前使用的活跃线程数
    JVM调优总结 -Xms -Xmx -Xmn -Xss(1)
    java高并发之线程池
    Nginx 网络事件
  • 原文地址:https://www.cnblogs.com/Dyleaf/p/8507015.html
Copyright © 2011-2022 走看看