一、介绍

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. Nginx的启动、停止等命令

    Windows下Nginx的启动.停止等命令 在Windows下使用Nginx,我们需要掌握一些基本的操作命令,比如:启动.停止Nginx服务,重新载入Nginx等,下面我就进行一些简单的介绍.1.启 ...

  2. 关于MQ的几件小事(三)如何保证消息不重复消费

    1.幂等性 幂等(idempotent.idempotence)是一个数学与计算机学概念,常见于抽象代数中. 在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同.幂等函数,或 ...

  3. NodeList和HTMLCollection区别

    关于DOM集合接口,主要不同在于HTMLCollection是元素集合而NodeList是节点集合(既包括元素,也包括节点). 规定一下结果是: . node.childNodes 结果返回类型是 N ...

  4. Machine Learning Technologies(10月20日)

    Linear regression SVM(support vector machines) Advantages: ·Effective in high dimensional spaces. ·S ...

  5. Traceback (most recent call last): File "../zubax_chibios/tools/make_boot_descriptor.py", line 251

    出现如下错误: Traceback (most recent call last): File "../zubax_chibios/tools/make_boot_descriptor.py ...

  6. coco creator编辑动画坑之拖图片

    如图所示,批量选中多张图片之后,拖到右下角的框中,发现有时候可以有时候不行.这个我觉得是个软件的bug 后来经过测试发现只有在粉红色圈的高度才可以正确放入图片,否则都放不了.

  7. Linux/Aix日常报错整理

    [root@localhost ~]# umount /mnt umount.nfs: /mnt: device is busy umount.nfs: /mnt: device is busy 问题 ...

  8. “key_share”和"pre_shared_key" 的区别

    越是基本的问题或者概念越是难解释或者讲清楚,论文写到现在真的好无助的样子 . 在LTS协议中牵扯到握手的认证和秘钥建立的时候,这连个概念经常混淆,TLS1.3版本协议为参照,区别这两个概念 1.概念的 ...

  9. MySQL分布式数据库架构:分库、分表、排序、分页、分组、实现教程

    MySQL分库分表总结: 单库单表 : 单库单表是最常见的数据库设计,例如,有一张用户(user)表放在数据库db中,所有的用户都可以在db库中的user表中查到. 单库多表 : 随着用户数量的增加, ...

  10. Ansible批量自动化管理工具

    一,工具与环境介绍 1.1 ansible简介 批量管理服务器的工具 无需部署agent,通过ssh进行管理 流行的自动化运维工具:https://github.com/ansible/ansible ...