前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式之单例模式

设计模式之单例模式

作者头像
Dylan Liu
发布2019-07-23 09:55:37
3250
发布2019-07-23 09:55:37
举报
文章被收录于专栏:dylanliudylanliu

定义

单例模式(Singleton Pattern)限制系统中某一个类只能有一个唯一的实例。很多时候系统对类的需求就只是一个全局对象,有些资源比较重,加载创建耗时,适用于单例模式;有些资源代表的是纯函数的操作,虽然可以使用new 来创建新对象,使用单例模式可以减少对象创建消耗,在手机等资源少的地方推荐使用。

使用场景

  • 资源加载创建耗时,而资源是可共享的,比如日志文件,应用配置,打印机、显示器等
  • 系统资源,如线程池
  • 可复用的类资源,如纯函数的 Service,工具类
  • 控制访问需求,如一个类要控制类实例的权限
  • 全局的状态,比如统计访问人数,系统的状态转换等

单例模式的创建方式

通常的单例获取是类提供一个 getInstance 的静态方法,外部对象使用静态方法获取到类实例。

预加载

在类加载后直接实例化单例,系统控制并发,缺点是如果这个类从始至终都没有被使用过,加载实例浪费了系统资源。

代码语言:javascript
复制
public final class Singleton {
    private static Singleton instance = new Singleton();
    
    private Singleton() {
        
    }
    
    public static Singleton getInstance() {
        return instance;
    }
}

懒加载(延迟加载)

针对预加载可能浪费资源的问题,可以使用懒加载来避免。懒加载只有在资源被使用到的时候才会去加载。

代码语言:javascript
复制
public final class Singleton {
    private static Singleton instance;
    
    private Singleton() {
        
    }
    
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

如果存在多线程并发的情况,上面的获取实例方法存在竞态条件,有两种解决方案。

  1. synchronized 同步方法
代码语言:javascript
复制
public final class Singleton {
    private static Singleton instance;
    
    private Singleton() {
        
    }
    
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

由于加了同步,因此多线程访问时只能串行访问,牺牲了效率保证了安全。

  1. double check lock
代码语言:javascript
复制
public final class Singleton {
    private static Singleton instance;

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

双重检查保证了只有实例还未初始化时才会竞争锁,当实例初始化后,与第一种懒加载方式执行路径是一样的,并且减少了锁争抢开销。(jdk1.5 之后才可以使用)

懒加载虽然保证了资源加载就一定是被使用的,但是如果资源加载时间较长,所以使用方都必须等待资源加载完成,譬如数据库的加载、连接建立,因此要看自己的使用场景来判断是否需要使用懒加载模式。

静态内部类

静态内部类严格说起来属于懒加载,使用了jvm 加载类的机制,避免了使用双重检测的方式来防止并发问题。

代码语言:javascript
复制
public final class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

    public static synchronized Singleton getInstance() {
        return Holder.holder;
    }

    private static class Holder {
        private static Singleton holder = new Singleton();
    }
}

Singleton 类加载时内部类并没有被加载,因此内部类的 holder 未被实例化,调用 getInstance 方法时,才真正加载和实例化。

枚举

枚举天生自带的单一属性非常符合单例模式,使用枚举的解决方案也非常优雅,而且还适合与策略、命令等模式配合。

枚举相比于前面的实现有几个优点:

  1. 无需费力将构造函数设为 private,而且设为了 private 也无法阻止使用方使用反射来创建实例。
  2. 无需担心序列化与反序列化,上面的模式中都没有考虑序列化相关问题,如果考虑还需要加入 readObject writeObject 方法。
  3. 虽然模式叫单例模式,但有时我们可能需要 n 个实例,数量是需要控制的,增加了实现难度,枚举只需要加一个枚举值
  4. 可以与策略模式相配合,实现更优雅。

当然枚举也是一种预加载的实现方案,适合简单的单例,对于复杂的单例实现像数据库引擎等的还是前面的方式更合适一些。

枚举的实现
  1. 简单枚举
代码语言:javascript
复制
public enum  Singleton {
    /**
     * 实例
     */
    INSTANCE;

    /**
     *
     */
    public void doSomething() {

    }
}
  1. 与策略、命令等模式结合 如果我们每个实例需要有不同的实现,那么可以在枚举实例中覆盖相关方法, 枚举也支持抽象方法。
代码语言:javascript
复制
public enum  SingletonEnum {
    /**
     * 实例
     */
    INSTANCE1 {
        @Override
        public void forOverride() {
            System.out.println("Instance1");
        }
    },
    INSTANCE2 {
        @Override
        public void forOverride() {
            System.out.println("Instance2");
        }
    };

    /**
     *
     */
    public void doSomething() {

    }

    public abstract void forOverride();
}

总结

单例模式在后端自己创建的场景已经不多了,Spring 框架完全代理了相关操作,为开发者提供了很大的遍历,但是 Spring 本质上是一个工厂,有些复杂场景还是需要自己来动手。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 定义
  • 使用场景
  • 单例模式的创建方式
    • 预加载
      • 懒加载(延迟加载)
        • 静态内部类
          • 枚举
            • 枚举的实现
        • 总结
        相关产品与服务
        文件存储
        文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档