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. .net core 使用log4net日志组件

    一个web站点必须要记录日志,否则哪里出错了,完全是黑娃找黑妹,两眼一抹黑. 最常用的就是log4net日志组件.我们可以通过扩展加入日志组件. 第一步:在项目中NuGet log4net包,即 In ...

  2. mac 常用终端命令

      mkdir 文件夹夹名称 创建一个文件夹 cd 文件夹名称 进入该文件夹 git init 在该文件夹下创建一个git仓库 touch 文件名称 在该文件夹下创建一个文件 echo '内容' &g ...

  3. 用canvas画一个房子

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <script t ...

  4. 学号 20175223 《Java程序设计》第4周学习总结

    学号 20175223 <Java程序设计>第4周学习总结 教材学习内容总结 第五章要点: 要点1:子类与父类:extends.类的树形结构: 要点2:子类的继承性:同一包中与不在同一包中 ...

  5. C#添加文字水印

    使用的是iTextSharp添加PDF水印,由于是接口动态生成PDF,所以采用的是全部是内存流的形式,而且水印是平铺是.iTextSharp版本是5.5 /// <summary> /// ...

  6. HTML知识点梳理1

    1,HTML基本结构 <!DOCTYPE html> <html> <head></head> <body> </body> & ...

  7. Python全栈之路----常用模块----time模块

    time 模块的方法 time.time():返回当前时间的时间戳. >>> import time >>> time.time() #从1974年到现在过去了多少 ...

  8. spring boot 1.4 整合 mybatis druid

    http://www.jianshu.com/p/cef49ad91ba9spring boot 1.4 整合 mybatis druid

  9. js post下载相当于 location.href

    /** *参数说明: url:下载地址,val:需要提交的参数值,具体类型和个数自行扩展 * 参数可以用obj = {url:""",val1:"111&quo ...

  10. ArrayUtil的创建和使用

    求数组最小值求数组最大值遍历数组元素求数组总和求数组平均数求数组反转实现数组的复制对数组进行排序 写入的方法和包: public class ArrayUtil { //求数组的最大值 public ...