【Java】多线程-单例模式/volatile-指令重排序

单例模式即代码中只有一个实例的模式

适用场景:有些场景下,有的类只能有一个对象,不能有多个

要注意:在单例模式下,要保证不能产生多个实例

1、饿汉模式

class Singleton{
    private static Singleton instance = new Singleton();

    public static Singleton getSingleton() {
        return instance;
    }

    private Singleton(){
    }
}

public class demo {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleton();
        Singleton singleton2 = Singleton.getSingleton();
        System.out.println(singleton1==singleton2);
        System.out.println(singleton1.equals(singleton2));
    }
}

将构造方法设为private,使得不能自己初始化类, 只能用默认给定的类

由于是单例,所以singleton1与singleton2地址内容都是一样的,两者是同一个实例

2、 懒汉模式

(1)懒汉模式-不安全

class SingletonLazy{
    private static SingletonLazy instance = null;

    public static SingletonLazy getSingletonLazy() {
        if(instance == null){
            instance = new SingletonLazy();
        }
        return instance;
    }

    private SingletonLazy(){
    }
}

public class demo {
    public static void main(String[] args) {
        SingletonLazy singletonLazy1 = SingletonLazy.getSingletonLazy();
        SingletonLazy singletonLazy2 = SingletonLazy.getSingletonLazy();
        System.out.println(singletonLazy1 == singletonLazy2);
        System.out.println(singletonLazy1.equals(singletonLazy2));
    }
}

饿汉模式中,无论原来有没有创建instance,都会初始化一个新的instance

懒汉模式为了节约开销,会在getSingletonLazy()方法中判断instance是否是null。如果不是null,说明instance已经初始化,只须直接返回即可;如果是第一次获取,那么instance没有初始化,是null,这时初始化后再返回

(2)懒汉模式-线程安全

上面的代码存在的问题是:由于判断if(instance == null)这一步和初始化instance = new SingletonLazy()这一步是分开的,有可能会出现线程安全问题

​​​​​​​

如上图,T1和T2都各创建了一个instance实例,从而出现了两个实例,破坏了单例模式的规则

为了解决上述线程安全问题,我们在getSingletonLazy()方法中对这段代码进行加锁(静态方法代码块加锁对象用类名.class)

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

(3)空间浪费

改成加锁代码后,产生了一个新问题,那就是:每次获得instance时都要进行加锁,但是加锁本身是一项耗费空间资源的操作,这样便会大大降低代码性能

如果我们能够得知实例已经创建,那么就不用再进行加锁了,所以在加锁之前我们对instance进行一次判断

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

        return instance;
}

注意,第一次判断和第二次判断作用并不一样:第一次是为了判断是否需要加锁;第二次是为了判断是否需要新创建实例

(4)指令重排序问题

经过两次优化,上述代码仍存在问题,那就是——指令重排序问题

 · 什么是指令重排序问题呢

当我们在new一个对象的时候,new这个过程分为三步

1.申请内存空间
2.在内存空间上构造对象 (构造方法)

3.把内存的地址,赋值给 instance 引用

这个过程可以按照123来执行,也可以按照132的顺序来执行 

在我们上述优化过的代码中,如果T1线程在new的过程中按照132的顺序来执行,那么在执行到第二步时,恰好T2第一次对instance进行判断,由于instance已经创建了实例,那么T2会直接返回这个没有构造对象的instance

如果代码中对返回的instance进行访问属性/方法等操作,程序就会报错

 · volatile

为了解决这个问题,我们使用了volatile关键字来对instance变量进行修饰

这样,在进行new操作时,就不会产生指令重排序问题

class SingletonLazy{
    private static volatile SingletonLazy instance = null;

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

volatile解决内存可见性问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/173090.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

人员定位系统人员聚集风险监测预警功能为企业安全生产保驾护航!

危化企业生产区域由于装置设施开停工、设备检维修或其它原因,会导致在局部作业区域出现作业操作人员、指挥管理人员不定时、不定点的聚集情况,一旦发生泄漏中毒、火灾爆炸、高温高热物质喷溅等事故,人员聚集将会在一定程度上加重事故后果的严…

【Echart】Echart设置label太长隐藏:

文章目录 第一种:竖排显示第二种:显示部分第三种:强制显示所有标签并旋转 第一种:竖排显示 xAxis: {type: category,data: res.data.data.sz.xAxis,axisLabel:{fontSize:12,formatter: function(value) {return value.split().joi…

nginx基础组件的使用

文章目录 一、Nginx 的相关组件介绍1.1、ngx_palloc相关源码1.2、ngx_array组件的相关源码1.3、ngx_array的数据结构1.4、ngx_cycle简介和相关源码1.5、ngx_list相关源码1.6、ngx_list 的数据结构 二、Nginx 组件的使用2.1、makefile的编写2.2、ngx_pallocngx_array的使用2.3、…

【代码随想录】刷题笔记Day33

前言 Day33虽说是一个月,但是从第一篇开始实际上已经过了8个月了,得抓紧啊 46. 全排列 - 力扣(LeetCode) 前面组合就强调过差别了,这道题是排序,因此每次要从头到尾扫,结合used数组 class So…

ky10 server aarch64 离线安装openssl3.1.4

离线程序 https://gitcode.net/zengliguang/ky10_aarch64_openssl_install.git 输入下面命令执行离线安装脚本 source openssl_offline_install.sh 安装完成

对比学习15篇顶会论文及代码合集,2023最新

对比学习(contrastive learning)是现在无监督学习中一种常用的学习机制,它可以在没有标签的数据上进行学习,避免依赖大量标签数据,从而帮助我们更好地理解和利用数据集中的信息,提高模型的性能和表现。 作…

做医疗影像心脏方面的看过来:医学图像重建的心脏 MRI 数据集

本文发布了CMRxRecon数据集,包括来自 300 名受试者的多对比度、多视图、多切片和多通道 CMR 成像数据,还由经验丰富的放射科医生提供了所有受试者的心肌和心室的手动分割。单位:复旦, 香港理工大学, 厦大等 心脏磁共振成像(CMR&a…

线程池有几种创建方式?

程序员的公众号:源1024,获取更多资料,无加密无套路! 最近整理了一波电子书籍资料,包含《Effective Java中文版 第2版》《深入JAVA虚拟机》,《重构改善既有代码设计》,《MySQL高性能-第3版》&…

【开源】基于微信小程序的音乐平台

项目编号: S 055 ,文末获取源码。 \color{red}{项目编号:S055,文末获取源码。} 项目编号:S055,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示 四、核心代码4.1 查询单首…

(02)vite环境变量配置

文章目录 将开发环境和生产环境区分开环境变量vite处理环境变量loadEnv 业务代码需要使用环境变量.env.env.development.env.test修改VITE_前缀 将开发环境和生产环境区分开 分别创建三个vite 的配置文件,并将它们引入vite.config.js vite.base.config.js import…

【OpenGauss源码学习 —— 列存储(ColumnTableSample)】

执行算子(ColumnTableSample) 概述ColumnTableSample 类ColumnTableSample::ColumnTableSample 构造函数ColumnTableSample::~ColumnTableSample 析构函数ExecCStoreScan 函数ColumnTableSample::scanVecSample 函数ColumnTableSample::getMaxOffset 函数…

「Qt Widget中文示例指南」如何创建一个计算器?(一)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写,所有平台无差别运行,更提供了几乎所有开发过程中需要用到的工具。如今,Qt已被运用于超过70个行业、数千家企业,支持数百万设备及应用。 本文将展示如何使用…

快速幂极简写法快速幂求逆元

快速幂原理介绍 快速幂模板 int qmi(int a, int k, int p) {int res 1;while (k) {//后面的a其实是底数与其指数的运算结果了,是不断迭代的//第一个a其实就是a的2的0次方if (k & 1) res (res * a) % p;a (a * a) % p;//注意,a是一个不断变化的过…

聊一聊Linux动态链接和GOT、PLT

共享动态库是现代系统的一个重要组成部分,大家肯定都不陌生,但是通常对背后的一些细节上的实现机制了解得不够深入。当然,网上有很多关于这方面的文章。希望这篇文章能够以一种与其他文章不同的角度呈现,可以对你产生一点启发。 …

【开源】基于Vue和SpringBoot的服装店库存管理系统

项目编号: S 052 ,文末获取源码。 \color{red}{项目编号:S052,文末获取源码。} 项目编号:S052,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 角色管理模块2.3 服…

什么是Jmeter ?Jmeter使用的原理步骤是什么?

1.1 什么是 JMeter Apache JMeter 是 Apache 组织开发的基于 Java 的压力测试工具。用于对软件做压力测试,它最初被设计用于 Web 应用测试,但后来扩展到其他测试领域。 它可以用于测试静态和动态资源,例如静态文件、Java 小服务程序、CGI 脚本…

Continuity” of stochastic integral wrt Brownian motion

See https://imathworks.com/math/math-continuity-of-stochastic-integral-wrt-brownian-motion/

5.基于飞蛾扑火算法(MFO)优化的VMD参数(MFO-VMD)

代码的使用说明 基于飞蛾扑火算法优化的VMD参数 优化算法代码原理 飞蛾扑火优化算法(Moth-Flame Optimization,MFO)是一种新型元启发式优化算法,该算法是受飞蛾围绕火焰飞行启发而提出的,具有搜索速度快、寻优能力强的…