单例模式的八种写法、单例和并发的关系

这篇具有很好参考价值的文章主要介绍了单例模式的八种写法、单例和并发的关系。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.单例模式的作用

为什么需要单例?

  • 节省内存和计算
  • 保证结果正确
  • 方便管理

2.单例模式的适用场景

  1. 无状态的工具类:比如日志工具类,不管是在哪里使用,我们需要的只是它帮我们记录日志信息,除此之外,并不需要在它的实例对象上存储任何状态,这时候我们就只需要一个实例对象即可。
  2. 全局信息类:比如我们在一个类上记录网站的访问次数,我们不希望有的访问被记录在对象 A 上,有的却记录在对象 B 上,这时候我们就让这个类成为单例。

3.饿汉式

静态常量(可用)

/**
 * 饿汉式(静态常量)(可用)
 */
public class Singleton1 {

    // 由于加了static关键字,根据JVM的规定,在类加载的时候就会完成INSTANCE的实例化,这样就避免了线程同步问题
    private final static Singleton1 INSTANCE = new Singleton1();

    // 构造函数是私有的
    private Singleton1() {

    }

    public static Singleton1 getInstance() {
        return INSTANCE;
    }
}

静态代码块(可用)

/**
 * 饿汉式(静态代码块)(可用)
 */
public class Singleton2 {

    private final static Singleton2 INSTANCE;

    // 与上一种写法类似,由JVM保证了线程安全
    static {
        INSTANCE = new Singleton2();
    }

    // 构造函数是私有的
    private Singleton2() {

    }

    public static Singleton2 getInstance() {
        return INSTANCE;
    }
}

4.懒汉式

线程不安全(不可用)

/**
 * 懒汉式(线程不安全)(不可用)
 */
public class Singleton3 {

    private static Singleton3 instance;

    // 构造函数是私有的
    private Singleton3() {

    }

    public static Singleton3 getInstance() {
        // 这种写法是线程不安全的,不可用
        if (instance == null) {
            instance = new Singleton3();
        }
        return instance;
    }
}

同步方法(线程安全,但不推荐用)

/**
 * 懒汉式(线程安全)(不推荐用)
 */
public class Singleton4 {

    private static Singleton4 instance;

    // 构造函数是私有的
    private Singleton4() {

    }

    // 这种写法虽然是线程安全的,但是效率太低,不推荐用
    public synchronized static Singleton4 getInstance() {
        if (instance == null) {
            instance = new Singleton4();
        }
        return instance;
    }
}

同步代码块(线程不安全,不可用)

/**
 * 懒汉式(线程不安全)(不可用)
 */
public class Singleton5 {

    private static Singleton5 instance;

    // 构造函数是私有的
    private Singleton5() {

    }

    public static Singleton5 getInstance() {
        // 这种写法并不是线程安全的,不可用
        if (instance == null) {
            synchronized (Singleton5.class) {
                instance = new Singleton5();
            }
        }
        return instance;
    }
}

双重检查 + volatile(推荐用)

优点:线程安全,延迟加载,效率较高。

/**
 * 双重检查 + volatile(推荐用)
 */
public class Singleton6 {

    // volatile防止重排序
    private volatile static Singleton6 instance;

    // 构造函数是私有的
    private Singleton6() {

    }

    public static Singleton6 getInstance() {
        // 双重检查保证线程安全
        if (instance == null) {
            synchronized (Singleton6.class) {
                if (instance == null) {
                    instance = new Singleton6();
                }
            }
        }
        return instance;
    }
}

为什么要用 volatile?

新建对象 rs = new Resource() 实际上有 3 个步骤:

  • construct empty resource()
  • call constructor
  • assign to rs

如下图所示,重排序会带来NPE问题(NullPointerException, 空指针异常),而使用 volatile 可以防止重排序。

单例模式的八种写法、单例和并发的关系,Java,单例模式,java,设计模式

静态内部类(推荐用)

/**
 * 静态内部类(线程安全,懒加载)(推荐用)
 */
public class Singleton7 {

    // 构造函数是私有的
    private Singleton7() {

    }

    // 由JVM的规定可知,这种写法同时满足了线程安全和懒加载两个优点
    private static class SingletonInstance {
        private static final Singleton7 INSTANCE = new Singleton7();
    }

    public static Singleton7 getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

枚举(推荐用)

单例模式的书写:

/**
 * 枚举(线程安全,懒加载)(推荐用)
 */
public enum Singleton8 {
    INSTANCE;

    public void whatever() {

    }
}

单例的使用:

Singleton8.INSTANCE.whatever();

哪种单例的实现方案最好?

Joshua Bloch 大神在《Effective Java》中明确表达过的观点:使用枚举实现单例的方法虽然还没有广泛采用,但是单元素的枚举类型已经成为实现 Singleton 的最佳方法。文章来源地址https://www.toymoban.com/news/detail-799856.html

  • 写法简单
  • 线程安全有保障
  • 懒加载
  • 避免反序列化破坏单例

到了这里,关于单例模式的八种写法、单例和并发的关系的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32 GPIO的八种工作模式各有特点,适用于不同的应用场景

    学了挺久的单片机老是记不住每种模式的运用场景今天用通义千问总结了一下作为鞭策顺便记录一下 STM32 GPIO的八种工作模式各有特点,适用于不同的应用场景。以下是每种模式的简要描述及其对应的应用场景: 1. **GPIO_Mode_AIN** - **模拟输入**    - **应用场景**: 当GPIO引脚作为

    2024年04月11日
    浏览(86)
  • Java List集合取交集的八种不同实现方式

    码到三十五 : 个人主页 心中有诗画,指尖舞代码,目光览世界,步履越千山,人间尽值得 ! 在Java中,取两个List集合的交集可以通过多种方式实现,包括使用Java 8的Stream API、传统的for循环遍历、使用集合的retainAll方法,以及使用Apache Commons Collections库等。 方法一:使用Jav

    2024年04月12日
    浏览(36)
  • 单例模式有几种写法?【如何实现单例模式?】

    专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客,如有问题交流,欢迎评论区留言,一定尽快回复!(大家可以去看我的专栏,是所有文章的目录) 文章字体风格: 红色文字表示:重难点★✔ 蓝色文字表示:思路以及想法★✔ 如果大家觉得有帮助的话,感谢大家帮

    2024年02月07日
    浏览(45)
  • Spring 事务失效的八种场景

    原因:Spring 默认只会回滚非检查异常 解法:配置 rollbackFor 属性 @Transactional(rollbackFor = Exception.class) 原因:事务通知只有捉到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知无法知悉 解法1:异常原样抛出: 在 catch 块添加 throw new RuntimeExc

    2024年02月14日
    浏览(40)
  • python安装包(模块)的八种方法

    easy_install 这应该是最古老的包安装方式了,目前基本没有人使用了。下面是 easy_install 的一些安装示例 pip 是最主流的包管理方案,使用 pip install xxx 就可以从 PYPI 上搜索并安装 xxx (如果该包存在的话)。 下面仅列出一些常用的 pip install 的安装示例 更多 pip 的使用方法,可

    2024年02月05日
    浏览(31)
  • 【Java|多线程与高并发】设计模式-单例模式(饿汉式,懒汉式和静态内部类)

    设计模式是一种在软件开发中常用的解决复杂问题的方法论。它提供了一套经过验证的解决方案,用于解决特定类型问题的设计和实现。设计模式可以帮助开发人员提高代码的可重用性、可维护性和可扩展性。 设计模式有很多,本文主要介绍单例模式. 单例模式是一种创建型设

    2024年02月11日
    浏览(55)
  • Mysql 提升索引效率优化的八种方法

    目录 1. 选择唯一性索引 2. 为经常需要排序、分组和联合操作的字段建立索引 3. 为常作为查询条件的字段建立索引 4. 限制索引的数目 5. 尽量使用数据量少的索引 6. 数据量小的表最好不要使用索引 7. 尽量使用前缀来索引 8. 删除不再使用或者很少使用的索引 总结 索引的设计可

    2024年04月26日
    浏览(43)
  • Selenium元素定位的八种方法(建议收藏)

    自动化一般需要四步操作:获取元素,操作元素,获取返回结果,断言(返回结果与期望结果是否一致),最后自动出测试报告。Selenium提供8种元素定位的方法:id,name,class name,link text,xpath,css selector,tag name ,partial link tex。 这八种元素定位方法用python语言表示为: find_element_b

    2024年02月09日
    浏览(39)
  • 【Flink】Flink 的八种分区策略(源码解读)

    Flink 包含 8 种分区策略,这 8 种分区策略(分区器)分别如下面所示,本文将从源码的角度解读每个分区器的实现方式。 GlobalPartitioner ShufflePartitioner RebalancePartitioner RescalePartitioner BroadcastPartitioner ForwardPartitioner KeyGroupStreamPartitioner CustomPartitionerWrapper 该分区器会将所有的数据都

    2024年04月10日
    浏览(36)
  • HTTP/1.1协议中的八种请求

    2023年8月29日,周二晚上 目录 概述八种请求 GET请求 POST请求 PUT请求 PATCH请求 DELETE请求 HEAD请求 OPTIONS请求 TRACE请求  HTTP/1.1协议中定义了8种常用的请求方法,分别是: 1. GET 用途:请求指定的页面信息,并返回实体主体。 例子:获取一个网页、图片等静态内容。 2. POST  用途:向指定

    2024年02月09日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包