设计模式-单例模式

单例模式的关键点:

  1. 构造方法不对外开放,为private
  2. 确保单例类只有一个对象,尤其是多线程情况下
  3. 通过静态类或枚举返回单例对象
  4. 确保单例类在反序列化的时候不会重新创建对象

懒汉式

如果没有用到该对象,会造成资源的浪费。调用效率高,但是不能延时加载

public class AAA{
    
    private static AAA a = new AAA();
    private AAA(){}
     /*
    * getInstance 添加了synchronized 关键字,,也就是说 getInstance 是一个同步方法,
    * 这就是懒汉式在多线程中保持唯一性的方式
    *
    * 懒汉式存在的问题是,即是instance已经被创建,每次调用getInstance方法还是会同步,这样就会浪费一些不必要的资源
    * */
    public static AAA getAAA(){
        return a;
    }
}

饿汉式

真正用到的时候才会进行创建,延时加载,避免资源浪费。但是方法同步,调用效率较低

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

双重锁检查

双重检查保证了安全性,但是由于jvm底层(指令重排序)原因,偶尔会出问题

public class Singleton{
    
    private static volatile Singleton singleton;
    
    private Singleton(){}
     /**
     * getInstance 进行了两次判空,第一次判空是为了不必要的同步,第二次判空为了在instance 为 null 的情况下创建实例
     * 既保证了线程安全且单例对象初始化后调用getInstance又不会进行同步锁判断
     * <p>
     * 优点:资源利用率高,效率高
     * 缺点:第一次加载稍慢,由于java处理器允许乱序执行,偶尔会失败
     *
     * @return
     */
    public static Singleton getInstance(){
        
        if(singleton == null){
            
            synchronized(this){
                if(singleton == null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton
    }
}

静态内部类实现

线程安全,调用效率高,可以延迟加载

public class Singleton{
    /*
    *当第一次加载Singleton类时并不会初始化singleton,只有第一次调用getInstance方法的时候才会初始化singleton
    *第一次调用getInstance 方法的时候虚拟机才会加载SingletonInner类,这种方式不仅能够保证线程安全,也能够保证对象的唯一,
    *还延迟了单例的实例化,所有推荐使用这种方式
    *内部类在类加载的时候并不会被加载,只有在调用getInstance()时才会被加载
    * */
    private static class SingletonInner{
        private static final SingletonInner singleton = new Singleton();
    }
    private Singleton(){}
    public static Singleton getInstance(){
        return SingletonInner.singleton;
    }
    
}

枚举实现

线程安全,调用效率高,不可延时加载,可以天然的防止反射和反序列化调用(独有)。

public enum singleton{
    //枚举元素本身就是单例
    INSTANCE;
    /**
     *枚举是写法最简单的
     * 默认枚举实例的创建时线程安全的,且在任何一种情况下它都是单例,包括反序列化
     * */
    public void singletonOperation(){
        
    }
    
}

使用容器实现

public class Singleton{
    
    public static Map<String,Object> map = new HashMap<String,Object>();
    
    private Singleton(){}
    //将多个单例对象放在一个map中,根据对应的key获取对应的对象
    //这样我们可以管理多种类型的单例,可以使用统一的接口进行获取操作,降低了使用成本,也隐藏了具体实现,降低了耦合度
    public static void registerInstance(String key,Object instance){
        if(!map.containsKey(key)){
            map.put(key,instance);
        }
    }
    public static Object getInstance(String key){
        return map.get(key);
    }
}
总结

-单例对象 占用资源少,不需要延时加载,枚举 好于 饿汉

-单例对象 占用资源多,需要延时加载,静态内部类 好于 懒汉式

反序列化会重新生成对象问题的解决方案

除枚举外都存在该问题所以需要加入如下代码防止反序列化生成对象

//防止反序列化获取多个对象的漏洞
//实现serializable接口,当从I/O流读取对象时,readResolve()方法会被优先调用到,可以在readResolve()中返回的对象中直接替换反序列化过程中创建的对象

private Singleton readResolve() throws ObjctStreamException{
    return Singleton.singleton;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值