【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解决内存可见性问题

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mybatis plus 简介

简介 MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 官网:https://baomidou.com/pages/24112f/ 特性 无侵入&…

《QT从基础到进阶·三十八》QWidget实现炫酷log日志打印界面

QWidget实现了log日志的打印功能,不仅可以在界面显示,还可以生成打印日志。先来看下效果,源码放在文章末尾: LogPlugin插件类管理log所有功能,它可以获取Log界面并能打印正常信息,警告信息和错误信息&…

runnergo全栈测试平台

一、全栈测试平台runnergo使用 官网 官方使用文档 二、单接口测试 三、性能测试 1.性能测试 2.性能测试报告 四、自动化测试(暂时不支持UI自动化,或许会上)

Jmeter 压测实战保姆级入门教程

1、Jmeter本地安装 1.1、下载安装 软件下载地址: https://mirrors.tuna.tsinghua.edu.cn/apache/jmeter/binaries/ 选择一个压缩包下载即可 然后解压缩后进入bin目录直接执行命令jmeter即可启动 1.2 修改语言 默认是英文的,修改中文,点击…

使用Java Servlet生成动态二维码

文章目录 引入ZXing库创建QRCodeServlet部署到Servlet容器拓展功能1. 动态生成二维码内容2. 调整二维码尺寸3. 错误修正级别4. 日志输出 结语 🎉欢迎来到Java学习路线专栏~探索Java中的静态变量与实例变量 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒🍹✨博客主页&…

【追求卓越04】数据结构--栈与队列

引导 今天我们开始学习栈与队列的内容,我觉得栈并不难,所以篇幅也就不会那么多了。在虚拟空间中,栈是用户空间中的一种数据结构,它主要用于保存局部变量。那么问题来了,为什么用栈来保存局部变量,不用别的数…

Go 语言函数、参数和返回值详解

函数是一组语句,可以在程序中重复使用。函数不会在页面加载时自动执行。函数将通过调用函数来执行。 创建函数 要创建(通常称为声明)一个函数,请执行以下操作: 使用 func 关键字。指定函数的名称,后跟括…

10年开发工程师总结,8大主流程序员兼职平台,月入30k不是梦!

今年互联网行业陆续裁员减薪,许多人怨声载道的同时也开始另谋出路。而对于程序员更是应该提早做好准备,活跃在兼职接单的最前沿。 我们程序员是一门技术工种,与互联网其他行业相比薪水会相对高一点,不过钱也不是那么好赚的&#…

2023年【广东省安全员B证第四批(项目负责人)】报名考试及广东省安全员B证第四批(项目负责人)复审考试

题库来源:安全生产模拟考试一点通公众号小程序 广东省安全员B证第四批(项目负责人)报名考试是安全生产模拟考试一点通总题库中生成的一套广东省安全员B证第四批(项目负责人)复审考试,安全生产模拟考试一点…

java:springboot单元测试spring-boot-starter-test

背景 Java的单元测试可以使用多个框架,其中比较流行的包括: JUnit:JUnit是Java单元测试最常用的框架,它提供了一套丰富的API,可以方便地编写测试用例和测试套件。JUnit 5是JUnit的最新版本,引入了许多新功…

各大电商平台双十一“狂飙”,如何选择商城系统?

今年是“双十一”的第十五年。作为各大平台和品牌的全年最重要的营销节点,品牌们可谓是来势汹汹,各种促销活动和优惠力度让人眼花缭乱。 淘天数据显示,天猫促销活动开售当晚,155个品牌开卖成交额突破1亿元;首小时内7.1…

str转wstr的三种方法和从网站获取json数据到数据随机提取,返回拼接字符串和动态数组

库的设置 hv库 外部包含目录:…\include\libhv_new\hv; 库目录:…\include\libhv_new\lib\x86\Release; 附加依赖项:hv.lib; //Get请求 获取json数据,然后提取符合 条件的,time值大于自定义变量的值,然后取…

【UE】用样条线实现测距功能(上)

目录 效果 步骤 一、创建样条网格体组件3D模型 二、实现点击连线功能 三、实现显示两点间距离功能 效果 步骤 一、创建样条网格体组件3D模型 创建一个圆柱模型,这里底面半径设置为10mm,高度设置为1000mm 注意该模型的坐标轴在如下位置&#xff1…

基于pytest的服务端http请求的自动化测试框架?

1、引言 我有一个朋友是做 Python 自动化测试的。前几天他告诉我去参加一个大厂面试被刷了。 我问他是有没有总结被刷下来的原因。他说面试官问了一些 pytest 单元测试框架相关的知识,包括什么插件系统和用力筛选。但是他所在的公司用的技术是基于 unittest 的&am…

Win10系统无法登录Xbox live的四种解决方法

在Win10系统中,用户可以登录Xbox live平台,畅玩自己喜欢的游戏。但是,有用户却遇到了无法登录Xbox live的问题。接下来小编给大家详细介绍四种简单的解决方法,解决后用户在Win10电脑上就能成功登录上Xbox live平台。 Win10系统无法…

Linux编程 文件操作 creat open

文件描述符 文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。 启动一个进程之后,…

SquareCTF-2023 Web Writeups

官方wp:CTFtime.org / Square CTF 2023 tasks and writeups sandbox Description: I “made” “a” “python” “sandbox” “”“” nc 184.72.87.9 8008 先nc连上看看,只允许一个单词,空格之后的直接无效了。 flag就在当…

inux应用开发基础知识——串口应用编程(十一)

前言: 在Linux系统中,串口设备以文件的形式存在,通常位于/dev目录下,如ttyS0、ttyUSB0等。这些设备文件可以用于读取和写入数据。要使用串口设备,需要打开相应的设备文件。在打开串口时,可以使用O_RDWR选项…

哈夫曼树你需要了解一下

哈夫曼树介绍哈夫曼数特点哈夫曼应用场景哈夫曼构建过程哈夫曼树示例拓展 哈夫曼树介绍 哈夫曼树(Huffman Tree)是一种特殊的二叉树,也被称为最优二叉树。在计算机科学中,它是由权值作为叶子节点构造出来的一种二叉树。哈夫曼树的…

05 取样器(BeanShell和JSR223 Sampler)

一、取样器作用 1、取样器可以理解为Jmeter的桥梁,或者是Jmeter的加工厂; 2、Jmeter使用过程中,经常有些数据不能直接使用,需要加工后才能使用;这样就用到了取样器;但是这里存在问题,Jmeter中的…