当前位置:

首页 > 编程开发 > Java单例模式中的线程安全问题怎么解决

Java单例模式中的线程安全问题怎么解决

一.使用多线程需要考虑的因素提高效率:使用多线程就是为了充分利用CPU资源,提高任务的效率线程安全:使用多线程最基本的就是保障线程安全问题所以我们在设计多线程代码的时候就必须在满足线程安全的前提下尽可能的提高任务执行的效故:加锁细粒度化:加锁的代码少一点,让其他代码可以并发并行的执行考虑线程安全:没有操作共享变量的代码没有安全问题对共享变量的读,使用volatile修饰变量即可对共享变量的写,使用synchronized加锁二.单例模式单例模式能保证某个类在程序中只存在唯一一份实例,而不会创建出多个实例例

    一. 使用多线程需要考虑的因素

    提高效率:使用多线程就是为了充分利用CPU资源,提高任务的效率
    线程安全:使用多线程最基本的就是保障线程安全问题

    所以我们在设计多线程代码的时候就必须在满足线程安全的前提下尽可能的提高任务执行的效
    故:
    加锁细粒度化:加锁的代码少一点,让其他代码可以并发并行的执行

    考虑线程安全:

    没有操作共享变量的代码没有安全问题
    对共享变量的读,使用volatile修饰变量即可
    对共享变量的写,使用synchronized加锁

    二. 单例模式

    单例模式能保证某个类在程序中只存在唯一一份实例,而不会创建出多个实例
    例如:DataSource(数据连接池),一个数据库只需要一个连接池对象

    单例模式分为饿汉模式和懒汉模式

    1. 饿汉模式

    饿汉模式是在类加载的时候就创建实例
    这种方式是满足线程安全的(JVM内部使用了加锁,即多个线程调用静态方法,只有一个线程竞争到锁并且完成创建,只执行一次)

    实现代码:

    public class Singleton {
        private static Singleton instance = new Singleton();
        private Singleton(){
     
        }
        public static Singleton getInstance(){
            return instance;
        }
    }

    2. 懒汉模式

    懒汉模式是在类加载的时候不创建实例,第一次使用的时候才创建

    实现代码:

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

    观察上述代码,在单线程下不存在线程安全问题,但是在多线程环境下存在安全问题吗? 

    分析:
    当实例没有被创建的时候,如果有多个线程都调用getInstance方法,就可能创建多个实例,就存在线程安全问题 
    但是实例一旦创建好,后面线程调用getInstance方法就不会出现线程安全问题

    结果:线程安全问题出现在首次创建实例的时候

    3. 懒汉模式(使用synchronized改进)

    我们使用sychronized修饰,????‍????️代码如下:

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

    这样实现线程安全存在什么问题呢?

    解析:
    我们对方法使用synchronized修饰,也就是每次调用该方法的时候都会竞争锁,但是创建实例只需要创建一次,也就是创建实例后,再调用该方法还需要竞争锁释放锁

    结果:虽然满足线程安全,但是效率低

    4. 懒汉模式(使用双重校验锁改进)

    在上述代码的基础上进行改动:

    使用双重if判定,降低竞争锁频率
    使用volatile修饰instance 

    实现代码:

    public class Singleton {
        private static volatile Singleton instance = null;
        private Singleton(){
     
        }
        public static synchronized Singleton getInstance(){
            if(instance == null){ //外层的if判断:如果实例被创建直接return,不让线程再继续竞争锁
                //在没有创建实例时,多个线程已经进入if判断了
                //一个线程竞争到锁,其他线程阻塞等待
                synchronized (Singleton.class) {
                    //内层的if判断,目的是让竞争失败的锁如果再次竞争成功的话判断实例是否被创建,创建释放锁return,没有则创建
                    if(instance == null){ 
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

    对双重if的解析:

    外层的if判断:实例只是被创建一次,当实例已经被创建好了就不要后续操作,直接return返回
    内层的if判断:实例未被创建时,多个线程同时竞争锁,只有一个线程竞争成功并创建实例,其他竞争失败的线程就会阻塞等待,当第一线程释放锁后,这些竞争失败的线程就会继续竞争,但是实例已经创建好了,所以需要再次进行if判断 

    画图分析,如下所示:

    Java单例模式中的线程安全问题怎么解决

    三. volatile的原理 

    volatile保证了可见性,有序性,在Java层面看,volatile是无锁操作,多个线程对volatile修饰的变量进行读可以并发并行执行,和无锁执行效率差不多

    volatile修饰的变量中,CPU使用了缓存一致性协议来保证读取的都是最新的主存数据 

    缓存一致性:如果有别的线程修改了volatile修饰的变量,就会把CPU缓存中的变量置为无效,要操作这个变量就要从主存中重新读取

    四. volatile的扩展问题(了解)

    如果说volatile不保证有序性,双重校验锁的写法是否有问题?

    关于new对象按顺序分为3条指令:

    (1) 分配对象的内存空间
    (2) 实例化对象
    (3) 赋值给变量

    正常的执行顺序为(1)(2)(3),JVM可能会优化进行重排序后的顺序为(1)(3)(2)

    这个重排序的结果可能导致分配内存空间后,对象还没有实例化完成,就完成了赋值
    在这个错误的赋值后,instance==null不成立,线程就会拿着未完成实例化的instance,使用它的属性和方法就会出错

    使用volatile保证有序性后:

    线程在new对象时不管(1)(2)(3)是什么顺序,后续线程拿到的instance是已经实例化完成的
    CPU里边,基于volatile变量操作是有CPU级别的加锁机制(它保证(1)(2)(3)全部执行完,写回主存,再执行其他线程对该变量的操作)

    本文内容来源于互联网,如有侵权请联系删除。
    作者最新文章
    编程开发
    相关文章 更多
    谷歌浏览器Mac版入口
    谷歌浏览器Mac版入口

    谷歌浏览器Mac版官方安装指南 谷歌浏览器Mac版官方安装入口是https://www.google.com/chrome/,需macOS 12+系统、500MB空间,下载.dmg后拖入应用程序安装,支持多设备同步、性能优化与隐私保护功能。 苹果电脑Chrome的安装入口究竟在哪里?这个问题最近可是

    Chrome浏览器JS脚本不运行怎么办
    Chrome浏览器JS脚本不运行怎么办

    Chrome中JavaScript未执行需依次检查:一、移除站点级禁用并添加允许域名;二、开启全局JavaScript开关;三、禁用干扰扩展;四、在开发者工具中启用JavaScript;五、重置内容设置为默认。 有时在Chrome里打开网页,会发现交互按钮点了没反应,数据加载不出来,页面仿佛“静止”

    IE浏览器怀旧版在线网址
    IE浏览器怀旧版在线网址

    IE浏览器怀旧版在线网址:一次精准的技术时光回溯 最近,不少老用户和怀旧爱好者在反复搜索一个问题:那个经典的Internet Explorer,如今还能在哪里原汁原味地体验到?答案指向一个特定的地址:https://ie.microsoft.com/legacy/。 这个网站远不止是一个简单的“皮肤

    火狐浏览器有哪些设置功能
    火狐浏览器有哪些设置功能

    火狐浏览器五大核心设置功能:解锁高效、安全与个性化体验 火狐浏览器功能强大,但如果不仔细挖掘,很多能大幅提升效率和安全性的设置可能就“藏着掖着”了。这就好比拥有一台高性能设备,却只用了基础模式。那么,如何把它调整到最顺手、最安全的状态?接下来,我们就聚焦于当前版本(截至2025年末)最关键的五大设置

    chrome搜索免验证入口
    chrome搜索免验证入口

    Chrome官方免验证入口为https://www.google.cn/chrome/,提供全平台安装包、免登录即用、本地化安全机制及引擎级性能优化。 到底该去哪里找正版、免费且无需繁琐验证的Chrome浏览器入口?这个问题困扰了不少网友。今天,我们就来直通核心,为大家详细拆解Chrome引擎的官方

    java heap space 选型思路:使用场景与区别整理
    java heap space 选型思路:使用场景与区别整理

    Java堆是JVM存储对象的核心内存区域,配置需结合场景:单体应用适中设置;大数据处理需大堆并关注GC停顿;微服务强调快速启动;高并发需精细划分堆区域。关键参数-Xms和-Xmx建议等值以稳定性能。垃圾回收器选择影响效率,如G1适用于大堆,ZGC可实现低停顿。内存错误时需监控堆状态。

    java heap space 使用中遇到的问题怎么解决
    java heap space 使用中遇到的问题怎么解决

    Java堆内存溢出错误通常因内存泄漏、数据处理需求过大或JVM参数配置不当引起。排查时可借助jmap、堆转储及MAT等工具定位问题。解决方案包括调整JVM内存参数(如-Xmx)、修复代码中的内存泄漏、优化大数据处理逻辑,并建立持续监控与预防机制,以保障应用稳定运行。

    java xml 选型思路:使用场景与区别整理
    java xml 选型思路:使用场景与区别整理

    XML在Java开发中用于配置、数据交换等场景。解析方式主要有DOM、SAX、StAX及第三方库。DOM适合操作小文件,SAX/StAX适合处理大文件流,JAXB用于对象与XML映射。选型需结合数据大小、内存、性能及团队熟悉度,现代框架常封装底层解析。

    using namespace 使用中遇到的问题怎么解决
    using namespace 使用中遇到的问题怎么解决

    命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

    c语言函数递归 实操经验总结:这些技巧很实用
    c语言函数递归 实操经验总结:这些技巧很实用

    理解递归的基本原理在C语言中,递归是一种函数调用自身的编程技术。要掌握它,首先需要理解其核心思想:将一个复杂的大问题,分解为一个或几个与原问题相似但规模更小的子问题,直到子问题足够简单,可以直接求解。这个过程通常包含两个关键部分:递归出口和递归体。递归出口定义了问题何时不再继续分解,即最简单、可直接

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    macOS软件
    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    Mac软件 更多
    灵活计算器
    灵活计算器
    macOS/iOS/Android

    灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

    赤友清理大师
    赤友清理大师
    macOS

    赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

    极度公式
    极度公式
    Windows/macOS/Linux

    极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

    WINDOWS 更多
    Windows 10
    Windows 10
    Windows

    Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

    极度公式
    极度公式
    Windows/macOS/Linux

    极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

    密码键盘
    密码键盘
    Windows/macOS/iOS/Android

    密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。