一、介绍

volatile保证共享变量的“可见性”。可见性指的是当一个线程修改变量时,另一个线程能读到这个修改的值。

这里就要提出几个问题。

  • 问题1:为什么一个线程修改时,另一个线程可能会“看不见”?
  • 问题2:这种可见性是如何实现的?

二、问题1 变量为何“不可见”

回答:是由于缓存导致的可见性问题

2.1 为什么要引入缓存?

是为了解决性能问题。CPU的处理速度远远快于内存的读取速度(CPU与内存之间的瓶颈也叫“冯诺依曼瓶颈”),所以处理器不直接和内存通信,而是设置内部缓存(L1、L2...),内存的数据会读取到内部缓存中,CPU直接从内部缓存中读取数据处理。

2.2 L1、L2、L3有何区别?

L1、L2、L3是CPU三级缓存,CPU缓存的定义为CPU与内存之间的临时数据交换器,它的出现是为了解决CPU运行处理速度与内存读写速度不匹配的矛盾。

读取速度 L1 > L2 > L3

容量大小 L1 < L2 < L3

L1又分为一级数据缓存、一级指令缓存,分别用于存放数据、执行数据的指令解码。

2.3 只管高速缓存中的变量,不管内存中的变量行不行?

多个处理器修改变量写到高速缓存,高速缓存中存储的变量值是最新,下次不管内存中的变量值,直接从高速缓存中获取不就行了?

回答:当然不行。

为什么?因为每个处理器都有自己的高速缓存,一个变量在A处理器的高速缓存中修改,B处理器可能感知不到。

三、问题2 变量如何变为“可见”

为了保证在多处理器下,缓存一致,就设置一个缓存一致的协议。每个处理器通过嗅探在总线传播的数据来检查自己缓存的值是否是过期的。

当处理器发现自己的缓存行对内的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当需要对数据进行修改时,重新从内存中读到高速缓存中。

3.1 具体是如何实现?

当变量被volatile修饰,编译成汇编时,会增加一个Lock前缀指令。

Lock前缀指令的作用:

  • Lock前缀指令,会让处理器缓存回写到内存
  • 一个处理器缓存回写到内存,导致其他处理器的缓存无效

参考文档

[1]: 《Java并发编程的艺术》
[2]: Java并发编程-volatile
[3]: CPU与内存的那些事
[4]: 超能课堂(133):为什么CPU缓存会分为L1、L2、L3?

Java核心复习—— volatile 与可见性的更多相关文章

  1. Java并发:volatile内存可见性和指令重排

    volatile两大作用 1.保证内存可见性 2.防止指令重排 此外需注意volatile并不保证操作的原子性. (一)内存可见性 1 概念 JVM内存模型:主内存和线程独立的工作内存 Java内存模 ...

  2. Java核心复习—— 原子性、有序性与Happens-Before

    一. 产生并发Bug的源头 可见性 缓存导致的可见性问题 原子性 线程切换带来的原子性问题 有序性 编译优化带来的有序性问题 上面讲到了 volatile 与可见性,本章再主要讲下原子性.有序性与Ha ...

  3. Java核心复习——J.U.C AbstractQueuedSynchronizer

    第一眼看到AbstractQueuedSynchronizer,通常都会有这几个问题. AbstractQueuedSynchronizer为什么要搞这么一个类? 这个类是干什么的.有什么用? 这个类 ...

  4. Java核心复习——线程池ThreadPoolExecutor源码分析

    一.线程池的介绍 线程池一种性能优化的重要手段.优化点在于创建线程和销毁线程会带来资源和时间上的消耗,而且线程池可以对线程进行管理,则可以减少这种损耗. 使用线程池的好处如下: 降低资源的消耗 提高响 ...

  5. Java核心复习 —— J.U.C 并发工具类

    一.CountDownLatch 文档描述 A synchronization aid that allows one or more threads to wait until* a set of ...

  6. Java核心复习——synchronized

    一.概念 利用锁机制实现线程同步,synchronized关键字的底层交由了JVM通过C++来实现 Java中的锁有两大特性: 互斥性 同一时间,只允许一个线程持有某个对象锁. 可见性 锁释放前,线程 ...

  7. Java核心复习——CompletableFuture

    介绍 JDK1.8引入CompletableFuture类. 使用方法 public class CompletableFutureTest { private static ExecutorServ ...

  8. Java核心复习—— ThreadLocal源码分析

    ThreadLocal,叫做线程本地存储,也可以叫做线程本地变量.ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量. 一.如何使用 class Acce ...

  9. Java核心复习——J.U.C LinkedBlockingQueue源码分析

    参考文档 LinkedBlockingQueue和ArrayBlockingQueue的异同

随机推荐

  1. template中实现加减乘除数学运算

    django模板只提供了加法的filter,没有提供专门的乘法和除法运算:django提供了widthratio的tag用来计算比率,可以变相用于乘法和除法的计算. # 加法{{value|add:1 ...

  2. H5 - 简学

    什么是HTML? HTML 是用来描述网页的一种语言. 0.HTML 指的是超文本标记语言 1.HTML 不是一种编程语言,而是一种标记语言 2.标记语言是一套标记标签 3.HTML 使用标记标签来描 ...

  3. MD 使用 i5ting_toc 转换成 HTML

    MD 使用 i5ting_toc 转换成 HTML 本文作者:天析 作者邮箱:2200475850@qq.com 发布时间: Wed, 10 Jul 2019 13:59:00 +0800 前言 md ...

  4. English-培训3-Please call me Beth

  5. cmake简单用法

    CMake是一个跨平台的编译工具,类似于automake 安装 # cd cmake-2.8.10.2 # ./bootstrap # make # make install project proj ...

  6. enumerateKeysAndObjectsUsingBlock 的用法

    block是一个有序列的指令代码块,通常在代码中间花括号括起来的objective-c代码,但是它能被传递和被分配局部变量,然后作为一个参数来传递,基本上可以把花括号中间的代码保存到数据结构里.看起来 ...

  7. Docker Compose编排工具部署lnmp实践及理论(详细)

    目录 一.理论概述 编排 部署 Compose原理 二.使用docker compose 部署lnmp 三.测试 四.总结 一.理论概述 Docker Compose是一个定义及运行多个Docker容 ...

  8. linux-centos7.6设置固定IP网络方法

    两种方法设置固定IP 本文分别用了虚拟机网络模式桥接模式和Net模式,至于两者直接的区别可查看其他文档. 一.安装时设置固定IP地址 1.在系统设置界面,点击“网络和主机名”选项,可以看到默认是未连接 ...

  9. 使用华为云+GitHub搭建自己的博客

    1.搭建自己博客首先创建GitHub账号 在GitHub官网上创建一个账号:   我的账号地址是 : https://github.com/SueKayTian 2.GitHub账号创建好之后,客户端 ...

  10. c++ 流对象之streambuf(可当做缓冲区使用)

    在C++ 中引入了流的概念,我们很方便的通过流来读写文本数据和二进制数据,那么流对象的数据究竟是怎么存储的呢,为了搞清这个问题,先来看一看c++ 的 io 体系: 由图可以看出,在stream 的实现 ...