案例1:是否存在我不是我的问题 flag==!flag     flag是boolean类型

了解volatile

概念

1、volatile如何保证内存可见性

2、volatile如何禁止指令重排序

3、内存屏障

4、内存可见性

5、关于volatile的单例模式

一、内存可见性

1.1 缓存一致性问题

  1、现代计算机系统在存储设备与处理器之间加了一层读写速度尽可能解决处理器运算速度的高速缓存

来作为内存与处理器之间的缓冲: 将运算需要使用到的数据复制到缓存中, 让运算能快速进行, 当运算结束

后再从缓存同步回内存之中, 这样处理器就无须等待缓慢的内存读写.

  2、缓存一致性问题:在多处理器系统中, 每个处理器都有自己的高速缓存, 而它们又共享同一主内存, 当多

个处理器的运算任务都涉及到同一个块主内存区域时, 将可能导致各自的缓存数据不一致.

1.2 内存模型

在特定的操作协议下, 对特定的内存或高速缓存进行读写访问的过程抽象.

1.3 内存可见性

  1、一个CPU核心对数据的修改, 对其他CPU核心立即可见.

  2、CPU修改数据, 首先是对缓存的修改, 然后再同步回主存, 在同步回主存的时候, 如果其

他CPU也缓存了这个数据, 就会导致其他CPU缓存上的数据失效, 这样当其他CPU再去它的

缓存读取这个数据的时候, 就必须从主存重新获取.

  3、实现原理一般是基于CPU的MESI协议, 其中E表示独占Exclusive, S表示Shared, M表示Modify,

I表示无效的Invalid, 如果一个CPU核心修改了数据, 那么这个CPU核心的数据状态就会更新为M, 同时其他

核心上的数据状态更新为I, 这个是通过CPU多核之间的嗅探机制实现的.

1.4 MESI(缓存一致性)

Modify、Exclusive、Shared、Invalid, 当CPU写数据时, 如果发现操作的变量是共享变量, 即在其他CPU中

也存在该变量的副本, 会发出信号通知其他CPU将该变量的缓存行为置为无效状态, 因此当其他CPU需要读取

这个变量时, 发现自己缓存中缓存的该变量的缓存行是无效的, 那么它就会从内存中重新读取.

1.5 嗅探机制

1、例如在x86处理器下, 将volatile变量修饰的共享变量的Java代码转换成汇编代码, 发现会多了lock修饰.

2、Lock前缀的指令在多核处理器下会引发以下事情.

3、将当前处理器缓存行的数据写回到系统内存

4、这个写回内存的操作将会使其它CPU里缓存了该内存地址的数据无效.

5、原理分析:为了提高处理速度, 处理器不直接和内存进行通信, 而是先将系统内存的数据读到内部缓存(L1, L2

或其它)后再进行操作, 但操作完就不知道何时会写到内存. 如果对声明了volatile的变量进行写操作, JVM会向处

理器发送一条lock前缀的指令. 将这个变量所在的缓存行的数据写回到系统内存. 在多处理器下, 为了保证各个处

理器缓存是一致的, 就会实现缓存一致性协议, 每个处理器通过嗅探在总线上传播的数据来检查自己的缓存的值

是否过期, 当处理器发现自己缓存行对应的内存地址被修改, 就会将当前处理器缓存行设置成无效状态, 当处理器

对这个数据进行修改操作的时候, 会重新从系统内存中把数据读到处理器缓存里.

1.6 volatile两条实现原则

1、Lock前缀指令会引起处理器缓存回写到内存:

Lock前缀指令导致在执行指令期间, 声言处理器的#LOCK信号. 在多处理器环境中, LOCK#信号确保在声言该信号期间, 处理器可以独占任何共享内存. 但是在最近的处理器里, LOCK#信号一般不锁总线, 而是锁缓存, 毕竟锁总线的开销比较大. 对于Intel486和Pentium处理器, 在锁操作时, 总是在总线上声言LOCK#信号. 但在P6和目前的处理器中, 如果访问的内存区域已经缓存在处理器内部, 则不会声言LOCK#信号. 相反, 它会锁定这块内存区域的缓存并回写到内存, 并使用缓存一致性机制来确保修改的原子性, 此操作被称为"缓存锁定", 缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据.

2、一个处理器的缓存回写到内存会导致其它的缓存无效:

  IA-32处理器和Intel64处理器使用MESI控制协议去维护内部缓存和其他处理器缓存的一致性. 在多核处理器系统中进行操作的时候, IA-32和Intel 64处理器能嗅探其他处理器访问系统内存和它们的内部缓存, 处理器使用嗅探技术保证它的内部缓存、系统内存和其他处理器的缓存的数据在总线上保持一致. 通过嗅探一个处理器来检测其他处理器打算写内存地址, 而这个地址当前处于共享状态, 那么正在嗅探的处理器将使它的缓存行无效, 在下次访问相同内存地址时, 强制执行缓存行填充.

1.7 Volatile与原子性的关系

Volatile限定的是从缓存读取时刻的校验.

1.8 原子性:记住结论

volatile可以禁止指令重排序,volatile并不能保证原子性;

1.9 volatile单例模式

public class Instance{

private static volatile Instance instance;

private Instance( ){

}

public static Instance getInstance( ) {

if(instance ==null){

instance =new Instance();

}

}

}

new Instance()分成三步原子指令://重点

1、new指令申请内存;

2、在申请的内存中进行Instance的初始化;

3、将申请的内存地址的引用赋值给instance变量;

虽然volatile可以禁止指令重排序, 让上面三个指令有序执行, 但是问题是volatile并不能保证原子性,

所以上面代码中可能出现的问题是当Thread-A执行到第二步进行new Instance初始化时, 此时还没

有将地址值赋给instance变量, 所以Thread-B此时看到的instance==null再次进入if中执行new Instance()操作,

但是为instance赋值堆中的引用是原子操作, 所以此时会进行校验instance是否已经被赋值.

所以假设上面代码被两个线程执行, 那么new Instance()会执行两次, 但是instance赋值操作只会执行一次

综上:总结volatile

初识volatile的更多相关文章

  1. Java并发专题(三)深入理解volatile关键字

    前言 上一章节简单介绍了线程安全以及最基础的保证线程安全的方法,建议大家手敲代码去体会.这一章会提到volatile关键字,虽然看起来很简单,但是想彻底搞清楚需要具备JMM.CPU缓存模型的知识.不要 ...

  2. 学习笔记:java并发编程学习之初识Concurrent

    一.初识Concurrent 第一次看见concurrent的使用是在同事写的一个抽取系统代码里,当时这部分代码没有完成,有许多的问题,另一个同事接手了这部分代码的功能开发,由于他没有多线程开发的经验 ...

  3. 初识DSP

    初识DSP 1.TI DSP的选型主要考虑处理速度.功耗.程序存储器和数据存储器的容量.片内的资源,如定时器的数量.I/O口数量.中断数量.DMA通道数等.DSP的主要供应商有TI,ADI,Motor ...

  4. 8.初识Lock与AbstractQueuedSynchronizer(AQS)

    1. concurrent包的结构层次 在针对并发编程中,Doug Lea大师为我们提供了大量实用,高性能的工具类,针对这些代码进行研究会让我们对并发编程的掌握更加透彻也会大大提升我们队并发编程技术的 ...

  5. 初识JAVA语言

    推荐阅读:  我的CSDN  我的博客园  QQ群:704621321 前言        很多游戏开发者可能会有疑问,你会C#,JS,TS,为什么还要初识JAVA呢?有人可能会说,多学点对自己有好处 ...

  6. 初识Lock与AbstractQueuedSynchronizer(AQS)

    本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...

  7. 01_初识C语言

    第一章 - 初识C语言 基本了解C语言的基础知识,对C语言有一个大概的认识. 每个知识点就是简单认识,不做详细讲解. 1. 什么是C语言? C语言是一门通用计算机编程语言,广泛应用于底层开发.C语言的 ...

  8. [C++基础入门] 1、C++初识

    文章目录 1 C++初识 1.1 第一个C++程序 1.1.1 创建项目 1.1.2 创建文件 1.1.3 编写代码 1.1.4 运行程序 1.2 注释 1.3 变量 1.4 常量 1.5 关键字 1 ...

  9. 初识JavaScript

    JavaScript ECMA-262: 变量,函数,对象,数据类型....唯独没有输入和输出. Javascript:包含 ECMA-262,核心 BOM 浏览器对象模型, DOM 文档对象模型 什 ...

  10. Android动画效果之初识Property Animation(属性动画)

    前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...

随机推荐

  1. 一站式微服务治理中台,Water v2.10.2 发布

    Water(水孕育万物...) Water 为项目开发.服务治理,提供一站式解决方案(可以理解为微服务架构支持套件).基于 Solon 框架开发,并支持完整的 Solon Cloud 规范:已在生产环 ...

  2. Mysql 事务隔离级别和锁的关系

    我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式.同时数据库又是个高并发的应用,同一时间会有大量的并发访问,如果加锁过度,会极大的降低并发处理能力.所以对于 ...

  3. 我为什么推荐Nuxt3

    我为什么推荐Nuxt3? 大家好,我今天想和你们分享一个非常棒的前端框架--Nuxt3.自从我接触了Nuxt3,我发现它在前端开发领域具有很多优点.我想逐一向你们介绍Nuxt3的优势,并向大家推荐一些 ...

  4. 全网最佳IoT命令行超级工具箱|帮你轻松解决百万物联网设备测试和联调

    程序员离不开命令行,许多经典命令是每天必用的,比如ls 和 cd. 作为一个物联网开发和学习人员,IoT设备协议的测试联调是工作中很重要的一环!我有很多时刻都想拥有一个能集成常见物联网协议的客户端工具 ...

  5. Containerd 入门基础操作

    Containerd 被 Docker.Kubernetes  CRI 和其他一些项目使用 Containerd 旨在轻松嵌入到更大的系统中.Docker 在后台使用 containerd来运行容器. ...

  6. [Linux]常用命令之【nl/sed/awk/wc/xargs/perl】

    nl nl : 在linux系统中用来计算文件中行号. nl 可以将输出的文件内容自动的加上行号!其默认的结果与 cat -n 有点不太一样, nl 可以将行号做比较多的显示设计,包括位数与是否自动补 ...

  7. ChatGPT研究报告:AIGC带来新一轮范式转移

    以ChatGPT为代表的AIGC(人工智能生成内容)将成为新一轮范式转移的开始. 本文约4000字,目标是快速建立AIGC知识体系,含有大量的计算专业名词,建议阅读同时扩展搜索. 一.行业现状 1.概 ...

  8. JVM的内存分配及各种常量池的区别(静态常量池、运行时常量池、字符串常量池)

    JVM内存分配 先了解下JVM中的内存分配,此处以hotspot vm为例(官方jdk采用的vm) 程序计数器 栈 1. 虚拟机栈 2. 本地方法栈 Java堆 堆内存是各个线程共享的区域 方法区 它 ...

  9. Linux(六)进程管理

    Linux系统管理 linux中的进程与服务 进程:Linux中正在执行的程序或者命令 服务:Linux中一直存在.常驻内存的进程 守护进程:进程按照运行方式进行划分,又分为前台显示和后台显示的进程( ...

  10. Go语言实战: 即时通信系统(未完)

    使用Go语言构建一个即时通信系统,旨在锻炼Go语言编程能力 该通信系统至少能够允许用户能够在客户端进行公聊,即所发消息能被所有用户看到,也可发起私聊(即两个用户之间私密通信).同时,用户能够看到当前有 ...