Java Volatile的三大特性
本文通过学习:周阳老师-尚硅谷Java大厂面试题第二季 总结的volatile相关的笔记
volatile是Java虚拟机提供的轻量级的同步机制,三大特性为:
保证可见性、不保证原子性、禁止指令重排
一、保证可见性

import java.util.concurrent.TimeUnit;class MyData {//主物理内存volatile int number = 0;public void addTo60() {this.number = 60;}
}public class VolatileDemo {public static void main(String args []) {MyData myData = new MyData();new Thread(() -> {System.out.println(Thread.currentThread().getName() + "\t come in");try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}myData.addTo60();System.out.println(Thread.currentThread().getName() + "\t update number value:" + myData.number);}, "AAA").start();while(myData.number == 0) {}//说明AAA线程在睡眠3秒后,更新的number的值,重新写入到主内存,并被main线程感知到了System.out.println(Thread.currentThread().getName() + "\t mission is over");}
}
/*** AAA come in* AAA update number value:60* main mission is over //若number=0没被volatile修饰,则这句不打印*/
二、不保证原子性
1、代码示例

import java.util.concurrent.TimeUnit;class MyData {volatile int number = 0;public void addPlusPlus() {number ++;}
}public class VolatileDemo {public static void main(String args []) {MyData myData = new MyData();for (int i = 0; i < 20; i++) {new Thread(() -> {for (int j = 0; j < 1000; j++) {myData.addPlusPlus();}}, String.valueOf(i)).start();}// 需要等待上面20个线程都计算完成后,在用main线程取得最终的结果值// 这里判断线程数是否大于2,为什么是2?因为默认是有两个线程的,一个main线程,一个gc线程while(Thread.activeCount() > 2) {Thread.yield();//yield表示不执行}// 最终输出的值应该=20*1000=20000System.out.println(Thread.currentThread().getName() + "\t finally number value: " + myData.number);//19504}
}
2、数值丢失的原因?
线程1和2同时修改各自工作空间中的内容,因为可见性,需要重写入内存,但
线程1在写入的时候,线程2也同时写入,导致线程1的写入操作被挂起,导致
线程2先写,线程1后写,线程1的值覆盖了线程2的值,因此数据丢失。
n++这条命令,被拆分成了3个指令:
-getfield 从主内存拿到原始n
-iadd 进行加1操作
-putfileld 把累加后的值写回主内存
假如三个线程同时通过getfield命令,拿到主存中的n值,然后三个线程,各自在自己的工作内存中进行加1操作,但他们并发进行 iadd 命令的时候,因为只能一个进行写,所以其它操作会被挂起,假设1线程,先进行了写操作,在写完后,volatile的可见性,应该需要告诉其它两个线程,主内存的值已经被修改了,但是因为太快了,其它两个线程,陆续执行 iadd命令,进行写入操作,这就造成了其他线程没有接受到主内存n的改变,从而覆盖了原来的值,出现写丢失,这样也就让最终的结果少于200
3、解决办法(synchronized / AtomicInteger)
public synchronized void addPlusPlus() { number ++; } |
AtomicInteger atomicInteger = new AtomicInteger(); public void addAtomic() { atomicInteger.getAndIncrement(); } |
三、禁止指令重排
指令重排的代码示例
public class ResortSeqDemo { int a= 0; boolean flag = false; public void method01() { a = 1; flag = true; } public void method02() { if(flag) { a = a + 5; System.out.println("reValue:" + a); } } } | 【顺序执行】 a=1 flag=true a=a+5 顺序执行,打印reValue:6 【指令重排】 flag=true a=a+5 打印reValue:5 a=1 |
四、应用-单例模式
![]() | 方法1. synchronized ![]() 方法2. 禁用指令重排 + DCL双端检锁 ![]() DCL = Double Check Lock 双端检锁机制 ![]() |
public class SingletonDemo {private static volatile SingletonDemo instance = null;private SingletonDemo() {System.out.println(Thread.currentThread().getName() + "\t 我是构造方法SingletonDemo");}public static SingletonDemo getInstance() {if(instance == null) {synchronized (SingletonDemo.class) {if(instance == null) {instance = new SingletonDemo();}}}return instance;}public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(() -> {SingletonDemo.getInstance();}, String.valueOf(i)).start();}}
}
/*
* 0 我是构造方法SingletonDemo
*/
原因是在某一个线程执行到第一次检测的时候,读取到 instance 不为null,instance的引用对象可能没有完成实例化。因为 instance = new SingletonDemo();可以分为以下三步进行完成:
memory = allocate(); // 1、分配对象内存空间
instance(memory); // 2、初始化对象
instance = memory; // 3、设置instance指向刚刚分配的内存地址,此时instance != null
但是我们通过上面的三个步骤,能够发现,步骤2 和 步骤3之间不存在 数据依赖关系,而且无论重排前 还是重排后,程序的执行结果在单线程中并没有改变,因此这种重排优化是允许的。
memory = allocate(); // 1、分配对象内存空间
instance = memory; // 3、设置instance指向刚刚分配的内存地址,此时instance != null,但是对象还没有初始化完成
instance(memory); // 2、初始化对象
这样就会造成什么问题呢?
也就是当我们执行到重排后的步骤2,试图获取instance的时候,会得到null,因为对象的初始化还没有完成,而是在重排后的步骤3才完成,因此执行单例模式的代码时候,就会重新在创建一个instance实例
指令重排只会保证串行语义的执行一致性(单线程),但并不会关系多线程间的语义一致性
所以当一条线程访问instance不为null时,由于instance实例未必已初始化完成,这就造成了线程安全的问题, 因此需要引入volatile,来保证出现指令重排的问题,从而保证单例模式的线程安全性。
相关文章:

Java Volatile的三大特性
本文通过学习:周阳老师-尚硅谷Java大厂面试题第二季 总结的volatile相关的笔记volatile是Java虚拟机提供的轻量级的同步机制,三大特性为:保证可见性、不保证原子性、禁止指令重排一、保证可见性import java.util.concurrent.TimeUnit;class M…...

Android Compose——一个简单的Bilibili APP
Bilibili移动端APP简介依赖效果登录效果WebView自定义TobRow的Indicator大小首页推荐LazyGridView使用Paging3热门排行榜搜索模糊搜索富文本搜索结果视频详情合集信息Coroutines进行网络请求管理,避免回调地狱添加suspendwithContextGit项目链接末简介 此Demo采用A…...

二叉树的最近公共祖先【Java实现】
题目描述 现有一棵n个结点的二叉树(结点编号为从0到n-1,根结点为0号结点),求两个指定编号结点的最近公共祖先。 注:二叉树上两个结点A、B的最近公共祖先是指:二叉树上存在的一个结点P,使得P既是…...

关闭应用程序遥测,禁止Windows收集用户信息
目录 1. 先创建还原点,防止意外 2. 界面设置 3. 服务 (1) GPEdit.msc - 本地计算机策略 - 计算机配置 - 管理模板 - Windows 组件 - 应用程序兼容性 - 关闭应用程序遥测 - 已启用 (2) GPEdit.msc - 本地计算机策略 - 计算机配置 - 管理模板 - Windows 组件 - 数…...

【备战面试】每日10道面试题打卡-Day4
本篇总结的是Java集合知识相关的面试题,后续也会更新其他相关内容 文章目录1、HashMap在JDK1.7和JDK1.8中有哪些不同?2、HashMap 的长度为什么是2的幂次方?3、HashMap的扩容操作是怎么实现的?4、HashMap是怎么解决哈希冲突的&…...

热乎的面经——初出茅庐
⭐️前言⭐️ 本篇文章记录博主与2023.03.04面试上海柯布西公司,一面所被问及的面试问题,回答答案仅供参考。 🍉欢迎点赞 👍 收藏 ⭐留言评论 📝私信必回哟😁 🍉博主将持续更新学习记录收获&am…...

数据库中各种锁汇总
本文汇总简记数据库中的各种锁。 名称英文名称定义解释悲观锁Pessimistic Lock在访问数据前先加锁,防止其他事务的并发修改数据通过获取锁来保证数据的独占性,从而避免并发修改数据带来的问题。乐观锁Optimistic Lock在修改数据时先不加锁,而…...

p76 - Python 开发-内外网收集 Socket子域名DNS
数据来源 Python 开发相关知识点: 1.开发基础环境配置说明 Windows10Pycharm 2.Python 开发学习的意义 学习相关安全工具原理 掌握自定义工具及拓展开发解决实战中无工具或手工麻烦批量化等情况 在二次开发 Bypass,日常任务,批量测试利用…...

QCC51XX--eFush Key加密
https://blog.csdn.net/weixin_42162924/article/details/125828901?spm=1001.2014.3001.5502 在开始讲eFush Key加密操作之前,说一下这个操作的作用就是将自己的固件采用硬件的方式进行加密。 操作步骤 1.创建一个txt文本文件,参考文档“Qualcomm BlueSuite v3.1.4 Release…...

nginx http模块
1.模块依赖2. 模块的初始化2.1 location的定义location的定义包含以下几种location [ | ~ | ~* | ^~ ] uri { ... } location name { ... }:表示精确匹配,只有请求的url路径与后面的字符串完全相等时,才会命中,不支持location嵌套~ÿ…...

守护进程 || 精灵进程
目录 守护进程(deamon) || 精灵进程 特点 什么是前台进程组 把自己写的服务器deamon deamon代码 守护进程(deamon) || 精灵进程 特点 01. 他的PPID是1(附件特征)02. COMMAND --- 称为进程启动的命令03…...

Zookeeper3.5.7版本——客户端命令行操作(znode 节点数据信息)
目录一、命令行语法二、znode 节点数据信息2.1、查看当前znode中所包含的内容2.2、查看当前节点详细数据2.3、节点详细数据解释一、命令行语法 命令行语法列表 命令基本语法功能描述help显示所有操作命令ls path使用 ls 命令来查看当前 znode 的子节点 [可监听]-w 监听子节点变…...

如何写好单测
1、为什么要写单测? 单测即单元测试(Unit Test),是对软件的基本组成单元进行的测试,比如函数、过程或者类的方法。其意义是: 功能自测,发现功能缺陷自我Code Review测试驱动开发促进代码重构并…...

CDH-6.3.2内置spark-2.4.0的BUG
1. 背景 公司最近在新建集群,全部采用开源的大数据框架,并且将之前使用的阿里云的所有服务进行下线,其中就涉及到了旧任务的迁移。 2. 任务 2.1. 简述 我接手到一个之前的 spark 任务,是读取阿里 LogStore 数据,然…...

SpringCloud之ElasticSearch笔记
ElasticSearch 初识ElasticSearch ElasticSearch是什么 ElasticSearch一个基于Lucene的底层的开源的分布式搜索引擎,可用来实现搜索,日志统计,分析,系统监控 正向索引和倒排索引 正向索引:逐条扫描(my…...

数字图像学笔记 —— 17. 图像退化与复原(自适应滤波之「最小二乘方滤波」)
文章目录维纳滤波的缺点约束最小二乘方滤波给一个实际例子吧维纳滤波的缺点 维纳滤波(Wiener Filter),虽然是一种非常强大的退化图像还原算法,但是从实验过程我们也发现它存在着致命的缺陷,那就是要求输入退化系统的 …...

2023-03-05:ffmpeg推送本地视频至lal流媒体服务器(以RTMP为例),请用go语言编写。
2023-03-05:ffmpeg推送本地视频至lal流媒体服务器(以RTMP为例),请用go语言编写。 答案2023-03-05: 使用 github.com/moonfdd/ffmpeg-go 库。 先启动lal流媒体服务器软件,然后再执行命令: go…...

MathType7最新版免费数学公式编辑器
话说我也算是 MathType准资深(DB)用户了,当然自从感觉用DB不好之后,我基本上已经抛弃它了,只是前不久因为个别原因又捡起来用了用,30天试用期间又比较深入的折腾了下,也算是变成半个MathType砖家,coco玛奇朵简单介绍一下这款软件:在很可能看到这儿的你还没有出生的某个年月&…...

一文带你入门angular(中)
一、angular中的dom操作原生和ViewChild两种方式以及css3动画 1.原生操作 import { Component } from angular/core;Component({selector: app-footer,templateUrl: ./footer.component.html,styleUrls: [./footer.component.scss] }) export class FooterComponent {flag: b…...

单例设计模式共享数据问题分析、解决(c++11)设计多线程。
系列文章目录 单例设计模式共享数据问题分析、解决; 文章目录系列文章目录前言一、单例模式1.1 基本概念1.2 单例设计模式共享数据问题分析、解决1.3 std::call_once()介绍二、代码案例1.代码示例总结前言 关键内容:c11、多线程、共享数据、单例类 本章内容参考git…...

Embedding-based Retrieval in Facebook Search
facebook的社交网络检索与传统的搜索检索的差异是,除了考虑文本,还要考虑搜索者的背景。通用搜索主要考虑的是文本匹配,并没有涉及到个性化。像淘宝,youtube这些其实都是涉及到了用户自身行为的,除了搜索还有推荐&…...

xmu 离散数学 卢杨班作业详解【8-12章】
文章目录第八章 树23456810第九章46811第十章24567第十一章14571116第十二章131317第八章 树 2 (2) 设有k片树叶 2∗m2∗43∗3k2*m2*43*3k2∗m2∗43∗3k n23kn23kn23k mn−1mn-1mn−1 联立解得k9 T中有9片树叶 3 有三颗非同构的生成树 4 (1) c --abc e–abed f–dgf…...

Linux入门篇-权限管理
简介 用户管理也是和权限相关的知识点。权限的作用 权限对于普通文件和目录文件作用是不一样的 。[kioskfoundation0 ~]$ ls -l total 264 -rw-rw-r--. 2 kiosk kiosk 31943 May 29 2019 ClassPrep.txt -rw-rw-r--. 2 kiosk kiosk 7605 Jun 14 2019 ClassRHAPrep.txt -rw-rw-r…...

Linux(基于 Centos7) 常用操作
1.Linux 简介Linux 是一种 免费使用、自由传播的类 Unix 操作系统Linux操作系统内核,由林纳斯托瓦兹在1991年10月5日首次发布...Linux 是一套开源操作系统,它有稳定、消耗资源小、安全性高等特点大多数人都是直接使用 Linux 发行版(就是将 Li…...

Math类详解与Random类、三种随机数生成方式(java)
文章目录📖前言:🎀认识Random类🎀三种随机数生成方式🎀Math类的用途🎀Math类的方法📖前言: 本篇博客主要以介绍Math类的常用方法及认识Random类,及三种随机数生成方式 …...

Mac编译QT程序出现Undefined symbols for architecture x86_64
在Mac编写日志服务类, Logging_d.h内容如下 #pragma once #include <QLoggingCategory> Q_DECLARE_LOGGING_CATEGORY(hovering) Q_DECLARE_LOGGING_CATEGORY(creation) Q_DECLARE_LOGGING_CATEGORY(mouseevents) Q_DECLARE_LOGGING_CATEGORY(state) Q_DECLARE_LOGGING_C…...

蓝桥杯-李白打酒加强版
蓝桥杯-李白打酒加强版1、问题描述2、解题思路3、代码实现1、问题描述 话说大诗人李白, 一生好饮。幸好他从不开车。 一天, 他提着酒显, 从家里出来, 酒显中有酒 2 斗。他边走边唱: 无事街上走,提显去打酒。 逢店加一倍, 遇花喝一斗。 这一路上, 他一共遇到店 N 次…...

AtCoder Beginner Contest 292 (A - E) 记录第一场ABC
AtCoder Beginner Contest 292 A - E前言Q1 A - CAPS LOCKQ2 Yellow and Red CardQ3 Four VariablesQ4 D - Unicyclic ComponentsQ5 E - Transitivity前言 本来晚上在打Acwing周赛,最后一题Trie想不出来咋写,看群里有人说ABC要开始了,想着没…...

ubuntu安装使用putty
一、安装 安装虚拟机串口 sudo apt-get install putty sudo apt install -y setserial 二、使用 虚拟机连接串口 sudo setserial -g /dev/ttyS* 查看硬件对应串口 找到不是unknown的串口 sudo putty...

【CS144】Lab5与Lab6总结
Lab5与Lab6Lab汇总Lab5概述Lab6概述由于Lab5和Lab6相对比较简单(跟着文档一步一步写就行),于是放在一起做一个简单概述(主要是懒得写了…) Lab汇总 Lab5概述 lab5要求实现一个IP与Ethernet(以太网&#x…...