volatile知识点

---------------------------------------------------------------------------

1.volatile关键字是用来解决什么问题的? volatile是为了解决(不同线程的)内存的可见性

2.什么是内存的可见性。 因cpu的速度是远远高于内存的读写速度的,为了不让CPU等待,cpu与内存之间有一个高速缓存(多级寄存器),也就是有主存和工作内存的概念,线程直接操作的是工作内存而不是主存,不同的线程都会有自己的工作内存,所以造成了不同线程间的共享变量的不可见性。

3.volatile关键字产生了什么了什么变化。

  防止指令重排序,对共享变量读取时强制读主存,对共享变量写操作时会将工作内存的数据强行刷到主存中。

4.volatile 只保证可见性,并不保证原子性。

---------------------------------------------------------------------------

 

对于java开发工程师来说,并发编程一直是一个具有挑战性的技术,本章将给大家介绍一下volatile的原理。

下面介绍几个概念:

共享变量:共享变量是指可以同时被多个线程访问的变量,共享变量是被存放在堆里面,所有的方法内临时变量都不是共享变量。

重排序:重排序是指为了提高指令运行的性能,在编译时或者运行时对指令执行顺序进行调整的机制。重排序分为编译重排序和运行时重排序。编译重排序是指编译器在编译源代码的时候就对代码执行顺序进行分析,在遵循as-if-serial的原则前提下对源码的执行顺序进行调整。as-if-serial原则是指在单线程环境下,无论怎么重排序,代码的执行结果都是确定的。运行时重排序是指为了提高执行的运行速度,系统对机器的执行指令的执行顺序进行调整。

可见性:内存的可见性是指线程之间的可见性,一个线程的修改状态对另外一个线程是可见的,用通俗的话说,就是假如一个线程A修改一个共享变量flag之后,则线程B去读取,一定能读取到最新修改的flag。

说到这里,可能有些同学会觉得,这不是废话吗,线程A修改变量flag后,线程B肯定是可以拿到最新的值的呀。假如你真的这么认为,那么请运行一下以下的代码:

package test;

public class VariableTest {

    public static boolean flag = false;

    public static void main(String[] args) throws InterruptedException {
ThreadA threadA = new ThreadA();
ThreadB threadB = new ThreadB(); new Thread(threadA, "threadA").start();
Thread.sleep(1000l);//为了保证threadA比threadB先启动,sleep一下
new Thread(threadB, "threadB").start(); } static class ThreadA extends Thread {
public void run() {
while (true) {
if (flag) {
System.out.println(Thread.currentThread().getName() + " : flag is " + flag);
break;
}
} } } static class ThreadB extends Thread {
public void run() {
flag = true;
System.out.println(Thread.currentThread().getName() + " : flag is " + flag);
}
}
}

运行结果:

以上运行结果证明:线程B修改变量flag之后,线程A读取不到,A线程一直在运行,无法停止。

内存不可见的两个原因:

1、cache机制导致内存不可见

我们都知道,CPU的运行速度是远远高于内存的读写速度的,为了不让cpu为了等待读写内存数据,现代cpu和内存之间都存在一个高速缓存cache(实际上是一个多级寄存器),如下图:

线程在运行的过程中会把主内存的数据拷贝一份到线程内部cache中,也就是working memory。这个时候多个线程访问同一个变量,其实就是访问自己的内部cache。

上面例子出现问题的原因在于:线程A把变量flag加载到自己的内部缓存cache中,线程B修改变量flag后,即使重新写入主内存,但是线程A不会重新从主内存加载变量flag,看到的还是自己cache中的变量flag。所以线程A是读取不到线程B更新后的值。

2、除了cache的原因,重排序后的指令在多线程执行时也有可能导致内存不可见,由于指令顺序的调整,线程A读取某个变量的时候线程B可能还没有进行写入操作呢,虽然代码顺序上写操作是在前面的。

volatile的原理:

volatile修饰的变量不允许线程内部cache缓存和重排序,线程读取数据的时候直接读写内存,同时volatile不会对变量加锁,因此性能会比synchronized好。另外还有一个说法是使用volatile的变量依然会被读到cache中,只不过当B线程修改了flag之后,会将flag写回主内存,同时会通过信号机制通知到A线程去同步内存中flag的值。我更倾向于后者的解释,还望大神指导一下正确的答案。

但是需要注意的是,volatile不保证操作的原子性,请勿使用volatile来进行原子性操作。

深入理解volatile的更多相关文章

  1. 理解volatile

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

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

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

  3. Java并发编程原理与实战十二:深入理解volatile原理与使用

    volatile:称之为轻量级锁,被volatile修饰的变量,在线程之间是可见的. 可见:一个线程修改了这个变量的值,在另一个线程中能够读取到这个修改后的值. synchronized除了线程之间互 ...

  4. 从缓存行出发理解volatile变量、伪共享False sharing、disruptor

    volatilekeyword 当变量被某个线程A改动值之后.其他线程比方B若读取此变量的话,立马能够看到原来线程A改动后的值 注:普通变量与volatile变量的差别是volatile的特殊规则保证 ...

  5. 5.彻底理解volatile

    1. volatile简介 在上一篇文章中我们深入理解了java关键字synchronized,我们知道在java中还有一大神器就是关键volatile,可以说是和synchronized各领风骚,其 ...

  6. 正确理解volatile与happens-before

    1. 双重校验锁实现单例的问题 在延迟实现单例时,一般代码形式如下: public class Foo { private static volatile Foo instance; public s ...

  7. 彻底理解volatile,领悟其中奥妙

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

  8. 让你彻底理解volatile,面试不再愁

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

  9. 深入理解volatile原理与使用

    volatile:称之为轻量级锁,被volatile修饰的变量,在线程之间是可见的. 可见:一个线程修改了这个变量的值,在另一个线程中能够读取到这个修改后的值. synchronized除了线程之间互 ...

  10. 深入理解volatile关键字

    Java内存模型 想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的. Java内存模型规定了所有的变量都存储在主内存中.每条线程中还有自己的工作内存,线程的工作内存 ...

随机推荐

  1. 关于Java的特点之继承

    继承--解决之道 继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类(比如刚才的Student),在父类中定义这些相同的属性和方法,所 ...

  2. Spring 源码学习(1)—— 容器的基本实现

    最近在读Spring的源码,参考的是郝佳的<Spring源码深度解析>,这里把一些学习心得分享一下,总结的地方可能还有一些不完善,希望大家指教 IoC(控制反转)是Spring的特性之一, ...

  3. vscode 完全支持zeng code的写法

    一.快速编写HTML代码 1.  初始化 HTML文档需要包含一些固定的标签,比如<html>.<head>.<body>等,现在你只需要1秒钟就可以输入这些标签. ...

  4. 新建虚拟机并与XShell连接(配置网卡)

    新建虚拟机并与XShell连接 一.新建虚拟机 下一步 下一步 选择CentOS版本,我的CentOS镜像是6.5版本64位,所以我选择CentOS 6 64. 然后点击下一步 可以自行更改位置,然后 ...

  5. excel图片链接转图片

    Sub LoadImage() Dim HLK As Hyperlink, Rng As Range For Each HLK In ActiveSheet.Hyperlinks '循环活动工作表中的 ...

  6. 剑指Offer 12. 数值的整数次方 (其他)

    题目描述 给定一个double类型的浮点数base和int类型的整数exponent.求base的exponent次方. 题目地址 https://www.nowcoder.com/practice/ ...

  7. Java六大必须理解的问题

    Java六大必须理解的问题 对于这个系列里的问题,每个学Java的人都应该搞懂.当然,如果只是学Java玩玩就无所谓了.如果你认为自己已经超越初学者了,却不很懂这些问题,请将你自己重归初学者行列.内容 ...

  8. Spark访问Hive表

    知识点1:Spark访问HIVE上面的数据 配置注意点:. 1.拷贝mysql-connector-java-5.1.38-bin.jar等相关的jar包到你${spark_home}/lib中(sp ...

  9. mod_conference ESL控制三(程序)

    第一篇描述了mod_conference控制原理,第二篇描述了conference相关事件,接下来对esl内联程序做简要说明. 由于event-socket采用TCP协议,因此需要一个线程与frees ...

  10. Go语言for循环语句

    for循环语句解释与使用 首先我们定义sum:=0 格式:for i:=1;i<=100;i++{ sum+=i } for 循环100次,首先执行i=1,然后判断i<100,再执行sum ...