一文理解设计模式--单例模式(Singleton)

单例模式(Singleton)

Singleton模式确保每个Java类加载器只能有一个已有的实例并提供对其的全局访问,单例可以节省内存加快对象访问速度

适用场景

  • 需要频繁实例化然后销毁的对象
  • 创建对象耗时长或占用资源大,但又要经常用到的对象
  • 带有状态的工具类对象
  • 频繁访问数据库或文件的对象,一次实例化多次使用

单例种类

  1. 饿汉式:类加载的时候就被初始化,静态字段中实例化
  2. 懒汉式:延迟加载,调用getInstance()方法时实例化
  3. 双重检查锁:延迟加载,对上一个进行优化的方式
    以上都是线程安全的

代码示例

饿汉式

/**
 * 饿汉式,急切初始化静态实例保证线程安全
 * <p>
 * 优点:没有线程安全问题,简单
 * 缺点:提前初始化会延长类加载器加载类的时间;如果不使用会浪费空间;不能传递参数进行实例化
 *
 * @author July
 * @date 2020/10/21
 */
public final class HungrySingleton {

    /**
     * 类的静态实例,final的
     */
    private static final HungrySingleton INSTANCE = new HungrySingleton();

    /**
     * 私有构造,没有其它方式能初始化类
     */
    private HungrySingleton() {
    }

    /**
     * 静态方法提供用户调用以获取类的实例
     *
     * @return 单例对象
     */
    public static HungrySingleton getInstance() {
        return INSTANCE;
    }
}

懒汉式(私有内部类方式)

/**
 * 懒汉式,在需要的时候才初始化单例对象
 *
 * 优点:延迟加载的情况下解决线程安全问题(Effective Java推荐写法)
 *
 * @author July
 * @date 2020/10/21
 */
public final class LazyLoadedSingleton {

    /**
     * 私有构造
     */
    private LazyLoadedSingleton() {
        System.out.println("init LazyLoadedSingleton");
    }

    /**
     * @return 单例对象
     */
    public static LazyLoadedSingleton getInstance() {
        return HelperHolder.INSTANCE;
    }

    /**
     * 【重点】内部类HelperHolder被引用的时间不会早于getInstance()被调用的时间,
     * 所以线程安全,不需要特殊的语言结构(例如volatile或synchronized)。
     */
    private static class HelperHolder {
        public static final LazyLoadedSingleton INSTANCE = new LazyLoadedSingleton();
    }
}

懒汉式(synchronized方式)

/**
 * 单例是延迟加载,这里使用synchronized进行同步保证线程安全
 * <p>
 * 注意:如果是由反射创建的,那么将不会在同一个类加载器中创建一个单例,而是创建多个选项
 *
 * @author July
 * @date 2020/10/21
 */
public final class LazyLoadedSingleton2 {

    private static LazyLoadedSingleton2 instance;

    private LazyLoadedSingleton2() {
    }

    /**
     * 实例仅在第一次调用时创建
     */
    public static synchronized LazyLoadedSingleton2 getInstance() {
        if (instance == null) {
            instance = new LazyLoadedSingleton2();
        }
        return instance;
    }
}

双重检查锁

/**
 * 双重检查锁
 * <p/>
 * 参考:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
 *
 * @author July
 * @date 2020/10/21
 */
public final class DoubleCheckLockingSingleton {

    private static volatile DoubleCheckLockingSingleton instance;

    /**
     * 私有构造防止客户端实例化
     */
    private DoubleCheckLockingSingleton() {
        // note 防止通过反射调用实例化
        if (instance != null) {
            throw new IllegalStateException("Already initialized.");
        }
    }

    /**
     * 获取单例对象
     *
     * @return
     */
    public static DoubleCheckLockingSingleton getInstance() {
        // 局部变量将性能提高25%,《Effective Java, Second Edition》推荐, p. 283-284
        DoubleCheckLockingSingleton result = instance;
        // 单例双重检查
        // 第一重
        if (result == null) {
            synchronized (DoubleCheckLockingSingleton.class) {
                result = instance;
                // 第二重
                if (result == null) {
                    instance = result = new DoubleCheckLockingSingleton();
                }
            }
        }
        return result;
    }
}

FAQ

  • 单例多种实现方式中为什么要用双重检查锁的方式来实现单例?
    在延迟加载中,保证线程安全可以直接在getInstance()方法中直接使用synchronized来保证线程安全,但性能一个问题,也就是说每次调用getInstance()方法都需要线程同步(加锁其实只需要在第一次初始化的时候用到),性能不高

更多内容可以参考我的另一篇文章:单例模式之双重检查锁(double check locking)的发展历程

如果你喜欢我的文章,记得一键三连(不要下次一定)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我思知我在

原创不易,多多一键三连

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值