玩转设计模式:单例模式

单例模式

保证一个类仅有一个实例,并提供一个访问它的全局访问点

优点

由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决

常见的五种单例模式实现方式
  • 饿汉式: 线程安全,调用效率高,不能延时加载
  • 懒汉式: 线程安全,调用效率不高,可以延时加载
  • 双重检测锁: 由于JVM底层内部模型原因,偶尔会出问题,不建议使用
  • 静态内部类: 线程安全,调用效率高,可以延时加载
  • 枚举单例: 线程安全,调用效率高,不能延时加载。并且可以天然的防止反射和反序列化漏洞
饿汉式
public class SingletonDemo {
    private static /*final*/ SingletonDemo s = new SingletonDemo();
    private SingletonDemo(){} // 私有化构造器
    public static /*synchronized*/ SingletonDemo02 getInstance(){
        return s;
    }
}

static变量会在类装载时初始化,此时也不会涉及多个线程访问该对象的问题。虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题。因此可以省略synchronized关键字
注: 如果只是加载本类,而不是要调用getInstance(),甚至永远没有调用,则会造成资源浪费

懒汉式
public class SingletonDemo01 {
    private static SingletonDemo01 s;
    private SingletonDemo01(){} // 私有化构造器
    public static synchronized SingletonDemo01 getInstance(){
        if (s == null) {
            s = new SingletonDemo01();
        }
        return s;
    }
}

lazy load:延迟加载,懒加载,真正用的时候才加载
注: 资源利用率高,但是每次调用getInstance()方法都要同步,并发效率低

静态内部类:兼备了并发高效调用和延迟加载的优势
public class SingletonDemo04 {
    private static class SingletonClassInstance {
        private static final SingletonDemo04 instance = new SingletonDemo04();
    }
    public static SingletonDemo04 getInstance() {
        return SingletonClassInstance.instance;
    }
    private SingletonDemo04() {}
}

外部类没有static属性,则不会像饿汉式那样立即加载对象。只有真正调用getInstance()才会加载静态内部类。加载类是线程安全的,instancecstatic final类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全。

以上单例模式存在的问题

通过反射和反序列化都能破解上面几种实现方式

解决方案

public class SingletonDemo01 implements Serializable {
     private static SingletonDemo01 s;
     private SingletonDemo01() throws Exception{
         if(s!=null){
             throw new Exception("只能创建一个对象");
             // 通过手动抛出异常,避免通过反射创建多个单例对象!
         }
     } 
     // 私有化构造器
     public static synchronized SingletonDemo01 getInstance() throws Exception{
         if(s==null){
             s = new SingletonDemo01();
         }
         return s;
     }
     // 反序列化时,如果对象所在类定义了readResolve(),(实际是一种回调),定义返回哪个对象。
     private Object readResolve() throws ObjectStreamException {
         return s;
     }
 }
枚举实现单例
public enum SingletonDemo05 {
   /**
    * 定义一个枚举的元素,它就代表了Singleton的一个实例。
    */
    INSTANCE;
   /**
    * 单例可以有自己的操作
    */
    public void singletonOperation(){
    //功能处理
    }
}

注: 实现简单,枚举本身就是单例模式,由JVM从根本上提供保障,避免通过反射和序列化的漏洞

场景选择

占用资源少,不需要延时加载

  • 枚举式好于饿汉式

占用资源多,需要延时加载

  • 静态内部类好于懒汉式
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值