美文网首页
Java 单例模式

Java 单例模式

作者: 任教主来也 | 来源:发表于2017-02-07 15:13 被阅读20次

    单例模式是开发中较为常见也较为简单的一种设计模式,单例模式的实现有多种,每种都有自己的特点,在这里整理一下,从而可以从一定高度来认识单例模式并应用单例模式。

    单例模式

    1. 私有的构造方法
    2. 通过一个公有静态方法或枚举返回单例对象
    3. 确保多线程时单例对象有且只有一个
    4. 确保单例对象反序列化时不会重新构造对象

    一、饿汉模式

    缺点:在类加载时对象就生成,不管是否使用,类卸载时对象才被销毁
    优点:不存在线程同步问题,避免使用 synchronized 造成的性能问题

    // 饿汉模式 在类被加载时初始化单例对象,稍微有点消耗资源
    public class Singleton {
        
        private static final Singleton singleton = new Singleton();
    
        private Singleton() {
        }
    
        public static Singleton getInstanse() {
            return singleton;
        }
    }
    
    

    二,懒汉模式

    // 懒汉模式 在用户第一次调用时初始化单例对象,缺点,每次获取时都会同步判断,浪费资源
    public class Singleton {
        
        private static Singleton singleton;
    
        private Singleton() {
        }
    
        public static synchronized Singleton getInstanse() {
            if(singleton == null){
                singletor = new Singleton();
            }
            return singleton;
        }
    }
    
    

    三、双重判断 Double Check Lock DCL

    JDK 1.5 及之前可能发生双重锁失效问题,因为 sigleton = new Singleton() 并不是原子性操作,由于 Java 支持编译乱序,则可能发生如果 A 线程中先为 singleton 赋值为对象的内存地址而真实对象还未实例化,此时 B 线程过来由于 singleton 不为 null 所以 B 线程不会被 synchronized 影响,此时 B 直接得到 singleton ,但由于使用时 singleton 还未实例化结束,所以会发生问题。1.6 及以后的版本可以使用 volatile 关键字,使用 volatile 关键字修饰 singleton ,会保证 singleton 初始化成功后再赋值。

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

    四、静态内部类

    简单方便,利用 Java 类加载机制保障线程安全,是推荐的方式

    类加载时不会加载内部类

    public class Singleton {
        private Singleton() {
    
        }
    
        private static class InnerClass {
            public static final Singleton singleton = new Singleton();
        }
    
        public static Singleton getInstance() {
            return InnerClass.singleton;
        }
    }
    

    解决反序列化时新建单例的问题

    在类中重写 readResolve 方法,并在其中返回单例对象

    private Object readResolve() throws ObjectStreamException(){
        return singleton; // 如果不重写,默认是重新生成一个新的对象
    }
    

    五、枚举

    简单逼格高,开销大

    枚举在 Java 中与普通的类是一样的,可以有字段,还能有自己的方法。最重要的是枚举实例的创建是线程安全的,且在任何情况下它都是一个实例,即使反序列化时也是一个实例

    public enum  Singleton {
        INSTANCE;
        
        // 枚举中也可以跟正常类一样定义方法
        public void doSomethind(){}
    }
    

    六、使用容器实现单例模式

    初始化时将多种单例类型注入到一个统一的管理类中,在使用时根据 key 获取对应类型的对象。降低用户使用成本,也对用户隐藏了具体实现,降低耦合度。

    public class SingletonManager {
        private static final Map<String, Object> objMap = new HashMap<>();
    
        private SingletonManager() {
        }
    
        public static void putObj(String key, Object value) {
            if (objMap.containsKey(key)) return;
            objMap.put(key, value);
        }
    
        public static Object getObj(String key) {
            return objMap.get(key);
        }
    }
    

    相关文章

      网友评论

          本文标题:Java 单例模式

          本文链接:https://www.haomeiwen.com/subject/zzkvettx.html