一、介绍

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. alt和title的区别

    alt是html标签的属性,而title既是html标签,又是html属性. 在图像标签img中,除了常用的宽度width和高度height属性之外,还有两个比较重要并且也会用到的属性,就是alt和t ...

  2. 记一次有趣的tp5代码执行

    0x00 前言 朋友之前给了个站,拿了很久终于拿下,简单记录一下. 0x01 基础信息 漏洞点:tp 5 method 代码执行,payload如下 POST /?s=captcha _method= ...

  3. USB原理简单叙述

    USB简介: USB的几种版本: 1. USB 1.0:速度 1.5Mb/s 2. USB 1.1:速度 12Mb/s 3. USB 2.0:速度 60MbB/s 4. USB 3.0:速度 640M ...

  4. 关于#error

    很简单的一个东西,但是感觉使用价值没有太大.实现了以下,结果如下: 执行到#error语句的时候直接停止编译,在下面输出设定好的错误信息. 来自为知笔记(Wiz)

  5. Mycat学习-单独启动mycat

    Mycat下载地址:http://mycat.io/ Mycat安装:解压缩即可.   Mycat作为一个中间件,实现mysql协议,是可以不依赖数据库单独运行的. 对前端应用连接来说就是一个数据库, ...

  6. 五、MySQL系列之高级知识(五)

    本篇 主要介绍MySQL的高级知识---视图.事件.索引等相关知识: 一.视图 在学习视图时我们需要什么是视图,视图有哪些好处以及视图的相关操作: 1.1  什么是视图? 关于视图通俗来讲就是一条se ...

  7. centos 安装ELK

    准备安装环境 由于本人的centos是通过虚拟机来进行安装的,为了本地电脑能够访问centos系统中的端口,则需要把防火墙进行关闭,通过以下方式进行关闭防火墙. # vi /etc/sysconfig ...

  8. two pointers 思想

    针对有序的序列特性做出的优化思想

  9. Kotlin基础特性深入讲解

    继续学习基础语法,在上次https://www.cnblogs.com/webor2006/p/11183077.html中定义了一个两数相加的函数,如下: 其实对于这个函数还可以进一步简写,因为函数 ...

  10. <<回想>>

    算是一个简单的回忆录,文笔很差,愧对语文老师 突然发现上一篇回忆录,没错就是那个流水账,是去年今天写的...   这是2019年7月的一天,NOI2019刚刚落下帷幕,而小F,则百无聊赖地在高铁站等车 ...