您的位置:首页 > 教程 > JAVA/JAVA编程 > Java单例模式中的线程安全问题

Java单例模式中的线程安全问题

2022-06-20 11:01:16 来源:易采站长站 作者:

Java单例模式中的线程安全问题

目录
一. 使用多线程需要考虑的因素????二. 单例模式????1. 饿汉模式????2. 懒汉模式????3. 懒汉模式(使用synchronized改进)4. 懒汉模式(使用双重校验锁改进)三. volatile的原理 ????四. volatile的扩展问题(了解)

97i站长之家-易采站长站-Easck.Com

一.>

提高效率:使用多线程就是为了充分利用CPU资源,提高任务的效率97i站长之家-易采站长站-Easck.Com
线程安全:使用多线程最基本的就是保障线程安全问题97i站长之家-易采站长站-Easck.Com

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

????考虑线程安全:97i站长之家-易采站长站-Easck.Com

没有操作共享变量的代码没有安全问题97i站长之家-易采站长站-Easck.Com
对共享变量的读,使用volatile修饰变量即可97i站长之家-易采站长站-Easck.Com
对共享变量的写,使用synchronized加锁97i站长之家-易采站长站-Easck.Com

97i站长之家-易采站长站-Easck.Com

????二.>

单例模式能保证某个类在程序中只存在唯一一份实例,而不会创建出多个实例97i站长之家-易采站长站-Easck.Com
例如:DataSource(数据连接池),一个数据库只需要一个连接池对象97i站长之家-易采站长站-Easck.Com

单例模式分为饿汉模式和懒汉模式97i站长之家-易采站长站-Easck.Com

97i站长之家-易采站长站-Easck.Com

????1.>

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

实现代码:97i站长之家-易采站长站-Easck.Com

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

97i站长之家-易采站长站-Easck.Com

????2.>

懒汉模式是在类加载的时候不创建实例,第一次使用的时候才创建97i站长之家-易采站长站-Easck.Com

实现代码:97i站长之家-易采站长站-Easck.Com

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

观察上述代码,在单线程下不存在线程安全问题,但是在多线程环境下存在安全问题吗? 97i站长之家-易采站长站-Easck.Com

分析:97i站长之家-易采站长站-Easck.Com
????当实例没有被创建的时候,如果有多个线程都调用getInstance方法,就可能创建多个实例,就存在线程安全问题 97i站长之家-易采站长站-Easck.Com
????但是实例一旦创建好,后面线程调用getInstance方法就不会出现线程安全问题97i站长之家-易采站长站-Easck.Com

结果:线程安全问题出现在首次创建实例的时候97i站长之家-易采站长站-Easck.Com

97i站长之家-易采站长站-Easck.Com

????3.>

我们使用sychronized修饰,????‍????️代码如下:97i站长之家-易采站长站-Easck.Com

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

这样实现线程安全存在什么问题呢?97i站长之家-易采站长站-Easck.Com

解析:97i站长之家-易采站长站-Easck.Com
我们对方法使用synchronized修饰,也就是每次调用该方法的时候都会竞争锁,但是创建实例只需要创建一次,也就是创建实例后,再调用该方法还需要竞争锁释放锁97i站长之家-易采站长站-Easck.Com

结果:虽然满足线程安全,但是效率低97i站长之家-易采站长站-Easck.Com

97i站长之家-易采站长站-Easck.Com

4.>

在上述代码的基础上进行改动:97i站长之家-易采站长站-Easck.Com

使用双重if判定,降低竞争锁频率97i站长之家-易采站长站-Easck.Com
使用volatile修饰instance 97i站长之家-易采站长站-Easck.Com

实现代码:97i站长之家-易采站长站-Easck.Com

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的解析:97i站长之家-易采站长站-Easck.Com

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

画图分析,如下所示:97i站长之家-易采站长站-Easck.Com

97i站长之家-易采站长站-Easck.Com

97i站长之家-易采站长站-Easck.Com

三.>

volatile保证了可见性,有序性,在Java层面看,volatile是无锁操作,多个线程对volatile修饰的变量进行读可以并发并行执行,和无锁执行效率差不多97i站长之家-易采站长站-Easck.Com

volatile修饰的变量中,CPU使用了缓存一致性协议来保证读取的都是最新的主存数据 97i站长之家-易采站长站-Easck.Com

缓存一致性:如果有别的线程修改了volatile修饰的变量,就会把CPU缓存中的变量置为无效,要操作这个变量就要从主存中重新读取97i站长之家-易采站长站-Easck.Com

97i站长之家-易采站长站-Easck.Com

????四.>

????如果说volatile不保证有序性,双重校验锁的写法是否有问题?97i站长之家-易采站长站-Easck.Com

关于new对象按顺序分为3条指令:97i站长之家-易采站长站-Easck.Com

????(1) 分配对象的内存空间97i站长之家-易采站长站-Easck.Com
????(2) 实例化对象97i站长之家-易采站长站-Easck.Com
????(3) 赋值给变量97i站长之家-易采站长站-Easck.Com

正常的执行顺序为(1)(2)(3),JVM可能会优化进行重排序后的顺序为(1)(3)(2)97i站长之家-易采站长站-Easck.Com

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

使用volatile保证有序性后:97i站长之家-易采站长站-Easck.Com

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

 到此这篇关于Java单例模式中的线程安全问题的文章就介绍到这了,更多相关Java单例模式线程安全内容请搜索易采站长站以前的文章或继续浏览下面的相关文章希望大家以后多多支持易采站长站!97i站长之家-易采站长站-Easck.Com

如有侵权,请联系QQ:279390809 电话:15144810328

相关文章

  • Spring Cloud 整合Apache-SkyWalking实现链路跟踪的方法

    Spring Cloud 整合Apache-SkyWalking实现链路跟踪的方法

    什么是SkyWalking 查看官网https://skywalking.apache.org/ 分布式系统的应用程序性能监视工具,专为微服务、云原生架构和基于容器(Docker、K8s、Mesos)架构而设计。 安装 进入下载页面https://
    2020-06-18
  • 成功解决IDEA2020 Plugins 连不上、打不开的方法

    成功解决IDEA2020 Plugins 连不上、打不开的方法

    IntelliJ IDEA 2020.1 插件中心一直打不开,鉴于有部分同学反馈设置http proxy不能解决,所以可按以下顺序检查 一、设置 http proxy—勾上Auto-detect proxy setting,参照下图,加上地址 http://127.0.0
    2020-06-25
  • Java后台实现微信支付和微信退款

    Java后台实现微信支付和微信退款

    微信支付流程 都是我自己工作中开发的,亲测可用,不喜勿喷。 controller中我是这么写的,你们需要根据自己的业务需求改动。ResponseBean是我自己封装的,你们可以改成你们想要的形式
    2020-03-27
  • IDEA2020 1.1中Plugins加载不出来的问题及解决方法

    IDEA2020 1.1中Plugins加载不出来的问题及解决方法

    进入File-Setting 如图,取消勾选,点击确认后重启,点击了以后等一会就可以正常显示 ps:下面看下解决IDEA 2020.1.1 找不到程序包和符号 问题描述 IDEA 2020.1.1 maven项目build的时候报错,找
    2020-06-28
  • Intellij idea热部署插件JRebel的使用

    Intellij idea热部署插件JRebel的使用

    项目需求,一直用eclipse的我,也要改用IDEA了,一开始,很不习惯。经过几天的慢慢摸索和习惯之后,发现IDEA确实很好用。dark的界面是我喜欢的,智能的提示也让写代码不再枯燥。 遗
    2020-06-25
  • 详解基于IDEA2020.1的JAVA代码提示插件开发例子

    详解基于IDEA2020.1的JAVA代码提示插件开发例子

    之前因为项目组有自己的代码规范,为了约束平时的开发规范,于是基于2019.1.3版本开发了一个代码提示的插件。但是在把IDEA切换到2020.1版本的时候,却发现疯狂报错,但是网上关于
    2020-06-25
  • springboot + rabbitmq 如何实现消息确认机制(踩坑经验)

    springboot + rabbitmq 如何实现消息确认机制(踩坑经验)

    本文收录在个人博客:www.chengxy-nds.top,技术资源共享,一起进步 最近部门号召大伙多组织一些技术分享会,说是要活跃公司的技术氛围,但早就看穿一切的我知道,这 T M 就是为了刷
    2020-07-01
  • JetBrains IntelliJ IDEA 2020安装与使用教程详解

    JetBrains IntelliJ IDEA 2020安装与使用教程详解

    对于JetBrains IntelliJ IDEA 2020的认识 IntelliJ IDEA 2020是一款JAVA编程软件,捷克IntelliJ公司研发推出。该软件提供了一个非常强大的JAVA集成开发环境,不仅添加了对Records的完整代码洞察支持,
    2020-06-28