一文读懂 volatile 关键字
程序员私房菜
共 4888字,需浏览 10分钟
· 2020-07-28
作者:对弈
来源:https://www.cnblogs.com/MessiXiaoMo3334/p/12615823.html
volatile是Java虚拟机提供的轻量级的同步机制(“乞丐版”的synchronized)
保证可见性 不保证原子性 禁止指令重排
可见性
指当多个线程访问同一个变量时,如果其中一个线程修改了这个变量的值,其他线程能够立即看得到修改的值
验证可见性demo:
import java.util.concurrent.TimeUnit;
class MyData {
volatile int number = 0;
public void addTo60() {
number = 60;
}
}
public class VolatileDemo {
public static void main() {
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 updated number: " + myData.number);
}, "AAA").start();
while (myData.number == 0) {}
System.out.println(Thread.currentThread().getName() + "\t mission is over");
}
}
结果:
AAA come in
main mission is over
AAA updated number: 60
不保证原子性
原子性:程序中的所有操作是不可中断的,要么全部执行成功要么全部执行失败
不保证原子性正是volatile轻量级的体现,多个线程对volatile修饰的变量进行操作时,会出现容易出现写覆盖的情况(i++)
验证不保证原子性demo:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
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();
}
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + "\t finally number value: " + myData.number);
}
}
结果:
main finally number value: 19109
解决不保证原子性问题:Atomic
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
class MyData {
volatile int number = 0;
public void addPlusPlus() {
number++;
}
AtomicInteger atomicInteger = new AtomicInteger();
public void addAtmic() {
atomicInteger.getAndIncrement();
}
}
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.addAtmic();
}
}, String.valueOf(i)).start();
}
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + "\t finally number value: " + myData.number);
System.out.println(Thread.currentThread().getName() + "\t AtomicInteger type,finally number value: "
+ myData.atomicInteger);
}
}
结果:
main finally number value: 19746
main AtomicInteger type,finally number value: 20000
禁止指令重排
指令重排:为了提高程序运行效率,编译器可能会对输入指令进行重新排序,即程序中各个语句的执行先后顺序同代码中的顺序不一定一致。(但是它会保证单线程程序最终执行结果和代码顺序执行的结果是一致的,它忽略了数据的依赖性)
源代码 -> 编译器优化重排 -> 指令并行重排 -> 内存系统重排 -> 最终执行指令
volatile能够实现禁止指令重排的底层原理:
内存屏障(Memory Barrier):它是一个CPU指令。由于编译器和CPU都能够执行指令重排,如果在指令间插入一条Memory Barrier则会告诉编译器和CPU,任何指令都不能和该条Memory Barrier指令进行重排序,即通过插入内存屏障指令能够禁止在内存屏障前后的指令执行重排序 优化 内存屏障的另外一个作用是强制刷新各种CPU的缓存数据,因此任何CPU上的线程都能够读取到这些数据的最新版本。以上两点正好对应了volatile关键字的禁止指令重排序和内存可见性的特点 对volatile变量进行写操作时,会在写操作之后加入一条store屏障指令,将工作内存中的共享变量copy刷新回主内存中;对volatile变量进行读操作时,会在读操作之前加入一条load的屏障指令,从主内存中读取共享变量
应用场景:
高并发环境下DCL单例模式使用volatile
public class SingletonDemo {
private static volatile SingletonDemo instance = null;
private SingletonDemo() {
System.out.println(Thread.currentThread().getName() + "我是构造方法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();
}
}
}
JUC包下AtomicXxx类:原子类AtomicXxx中都有一个成员变量value,该value变量被声明为volatile,保证 AtomicXxx类的内存可见性,而原子性由CAS算法&Unsafe类保证,结合这两点才能让AtomicXxx类很好地替代synchronized关键字。
public class AtomicInteger extends Number implements java.io.Serializable {
// ...
private volatile int value;
// ...
}
最后免费给大家分享50个Java项目实战资料,涵盖入门、进阶各个阶段学习内容,可以说非常全面了。大部分视频还附带源码,学起来还不费劲!
附上截图。(下面有下载方式)。
项目领取方式:
扫描下方公众号回复:50,
可获取下载链接
???
?长按上方二维码 2 秒 回复「50」即可获取资料
点赞是最大的支持
评论
springboot第70集:字节跳动后端三面经,一文让你走出微服务迷雾架构周刊
创建一个使用Kubernetes (K8s) 和 Jenkins 来自动化 GitLab 前端项目打包的CI/CD流水线,需要配置多个组件。下面,我将概述一个基本的设置步骤和示例脚本,以帮助你理解如何使用这些工具整合一个自动化流程。前提条件确保你已经有:Kubernetes 集群:用于部署 Jenk
程序源代码
0
PyPy为什么能让Python比C还快?一文了解内在机制
我的小册:(小白零基础用Python量化股票分析小册) ,原价299,限时特价2杯咖啡,满100人涨10元。来源:机器之心「如果想让代码运行得更快,您应该使用 PyPy。」—— Python 之父 Guido van Rossum对于研究人员来说,迅速把想法代码化并查看其是否行得通至关重要。Pyth
菜鸟学Python
0
一文读懂大模型发展过程!
点击下方“JavaEdge”,选择“设为星标”第一时间关注技术干货!免责声明~任何文章不要过度深思!万事万物都经不起审视,因为世上没有同样的成长环境,也没有同样的认知水平,更「没有适用于所有人的解决方案」;不要急着评判文章列出的观点,只需代入其中,适度审视一番自己即可,能「跳脱出来从外人的角度看看现
JavaEdge
0
【第125期】一文读懂开源软件协议
概述 开源软件协议是规范开源软件分发和使用条件的法律文档。它们允许软件的源代码对公众开放,但同时也保护了原作者的权益。以下是一些最常用的开源协议,以及它们的主要特点。 MIT License MIT 许可证是最宽松的开源协议之一。它允许用户自由使用、复制、修改和分发软件,只要保留原作者的版权声明和
前端微服务
0
【深度学习】一文看懂注意力机制
注意力是一种在广泛的神经结构中使用的越来越流行的机制。由于这一领域的快速发展,仍然缺乏对注意力的系统概述。在本文中,讨论了以往工作的不同方面,注意力机制的可能用途,并描述了该领域的主要研究工作和公开挑战。往期精彩回顾适合初学者入门人工智能的路线及资料下载(图文+视频)机器学习入门系列下载机器学习及深
机器学习初学者
10
回调函数(callback)是什么?一文理解回调函数(callback)
一、什么是回调函数1.1、回调函数的定义和基本概念回调函数是一种特殊的函数,它作为参数传递给另一个函数,并在被调用函数执行完毕后被调用。回调函数通常用于事件处理、异步编程和处理各种操作系统和框架的API。基本概念:回调:指被传入到另一个函数的函数。异步编程:指在代码执行时不会阻塞程序运行的方式。事件
良许Linux
0
一文掌握微服务技术:概念、架构与实现
微服务(或称微服务架构)是一种云原生架构方法,在单个应用中包含众多松散耦合且可单独部署的小型组件或服务。这些服务通常拥有自己的技术栈,包括数据库和数据管理模型;通过一个REST API、事件流和消息代理组合彼此通信;以及按照业务能力进行组织,具有通常称为有界上下文的服务分隔线。本文来自“微服务技术:
架构师技术联盟
10
贾佳亚团队新模型对标ChatGPT+DALL-E 3王炸组合!读懂梗图刷爆榜单,代码复现数学函数
来源:新智元【导读】贾佳亚团队提出VLM模型Mini-Gemini,堪比GPT-4+DALL-E 3王炸组合,一上线就刷爆了多模态任务榜单!读得懂梗图,做得了学术,用代码就能复现数学函数图。刷爆多模态任务榜单,超强视觉语言模型Mini-Gemini来了!效果堪称是开源社区版的GPT-4+DALL-E
AI算法与图像处理
10