讲到Java并发,多线程编程,一定避免不了对关键字volatile的了解,那么如何来认识volatile,从哪些方面来了解它会比较合适呢?个人认为,既然是多线程编程,那我们在平常的学习中,工作中,大部分都接触到的就是线程安全的概念。而线程安全就会涉及到共享变量的概念,所以首先,我们得弄清楚共享变量是什么,且处理器和内存间的数据交互机制是如何导致共享变量变得不安全。

01

共享变量

精彩瞬间回顾

能够在多个线程间被多个线程都访问到的变量,我们称之为共享变量。共享变量包括所有的实例变量,静态变量和数组元素。他们都被存放在堆内存中。

02

处理器与内存的通信机制

精彩瞬间回顾

大家都知道处理器是用来做计算的,且速度是非常快的,而内存是用来存储数据的,且其访问速度相比处理器来说,是慢了好几个级别的。那么当处理器需要处理数据时,如果每次都直接从内存拿数据的话,就会导致效率非常低,因此在现代计算机系统中,处理器是不直接跟内存通信的,而是在处理器和内存之间设置了多个缓存,也就是我们常常听到的L1, L2, L3等高速缓存。具体架构如下所示:

处理器都是将数据从内存读到自己内部的缓存中,然后在缓存中对数据进行修改等操作,结束后再由缓存写到回主存中去。如果一个共享变量 X,在多线程的情况下,同时被多个处理器读到各自的缓存中去,当其中一个处理器修改了X的值,改成Y了,先写回了内存,而此时另外一个处理器,又将X改成Z,再写回内存,那这种情况下,数据就已经有问题了,这种因为多线程操作而导致的异常问题,通常我们就叫做线程不安全。

如上述两图所示,X的变量同时被不同的处理器修改成各自的Y和Z,那么如何避免这种情况呢?这就涉及到了Java内存模型中的可见性的概念。

03

Java内存模型之可见性

精彩瞬间回顾

可见性,意思就是说,在多线程编程中,某个共享变量在其中一个线程被修改了,其修改结果要马上能够被其他线程看到,拿上面的例子来说,也就是当X在其中一个处理器的缓存中被修改成Y了, 另一个处理器必须能够马上知道自己缓存中的X已经被修改成Y了,当此处理器要拿此变量去参与计算的时候,必须重新去内存中将此变量的值Y读到缓存中。而一个变量,如果被声明成violate,那么其就能保证这种可见性,这就是volatile变量的作用了。

04

volatile

精彩瞬间回顾

那么 volatile 变量能够保证可见性的实现原理是什么?声明成volatile的变量,在编译成汇编指令的时候,会多出以下一行:

0x0bca13ae:lock addl $0x0,(%esp)      ;

一句指令的意思是在寄存器上做一个+0的空操作,但这条指令有个Lock前缀。而处理器在处理Lock前缀指令时,其实是声言了处理器的Lock#信号。在之前的处理器中,Lock#信号会导致传输数据的总线被锁定,其他处理器都不能访问总线,从而保证处理Lock指令的处理器能够独享操作数据所在的内存区域。但由于总线被锁住,其他的处理器都被堵住了,影响多处理器执行的效率。在后来的处理器中,声言Lock#信号的处理器,不会再锁住总线,而是检查到数据所在的内存区域,如果是在处理器的内部缓存中,则会锁定此缓存区域,将缓存写回到内存当中,并利用缓存一致性的原则来保证其他处理器中的缓存区域数据的一致性。

05

缓存一致性

精彩瞬间回顾

缓存一致性原则会保证一个在缓存中的数据被修改了,会保证其他缓存了此数据的处理器中的缓存失效,从而让处理器重新去内存中读取最新修改后的数据。在实际的处理器操作中,各个处理器会一直在总线上嗅探其内部缓存区域中的内存地址在其它处理器的操作情况,一旦嗅探到某处理器打算修改某内存地址,而此内存地址刚好也在自己内部的缓存中,则会强制让自己的缓存无效。当下次访问此内存地址的时候,则重新从内存当中读取新数据。volatile不仅保证了共享变量在多线程间的可见性,其还保证了一定的有序性。

06

有序性

精彩瞬间回顾

何谓有序性呢?事实上,java程序代码在编译器阶段和处理器执行阶段,为了优化执行的效率,有可能会对指令进行重排序。如果一些指令彼此之间互相不影响,那么就有可能不按照代码顺序执行,比如后面的代码先执行,而之前的代码则慢执行,但处理器会保证结束时的输出结果是一致的。以上的这种情况就说明指令有可能不是有序的。volatile变量,上面我们看过其汇编指令,会多出一条Lock前缀的指令,这条指令能够 保证,在这条指令之前的所有指令全部执行完毕,而在这条指令之后的所有指令全部未执行,也相于在这里立起了一道栅栏,称之为内存栅栏,而更通俗的说法,则是内存屏障。那么有了这道屏障,volatile变量就禁止了指令的重排序,从而保证了指令执行的有序性。所有对volatile变量的读操作一定发生在对volatile变量的写操作之后。这同时也说明了volatile变量在多个线程之间能够实现可见性的原理。所以各种规定和操作,其实之间互有关联,彼此依赖,才能更好地保证指令执行的准确和效率。

07

内存屏障

精彩瞬间回顾

在上面我们也引出了内存屏障的概念,也知道了,其实它就是一组处理器的操作指令。插入一个内存屏障,则相当于告诉处理器和编译器先于这个指令的必须先执行,后于这个指令的必须后执行。image内存屏障另一个作用是强制更新一次不同CPU的缓存。例如,一个写屏障会把这个屏障前写入的数据刷新到缓存,这样任何试图读取该数据的线程将得到最新值,而不用考虑到底是被哪个cpu核心或者哪颗CPU执行的。这再仔细一想,不就是上面所说的volatile的作用吗?所以,内存屏障,可见性,有序性,缓存一致性原则,在java并发中各种各样的名词,本质上可能就只是同一种现象或者同一种设计,从不同的角度观察和探讨所得出的不同的解释

java 读取内存地址结构体_Java并发系列之volatile相关推荐

  1. java反射怎么获取结构体_java反射-使用反射获取类的所有信息

    在OOP(面向对象)语言中,最重要的一个概念就是:万事万物皆对象. 在java中,类也是一个对象,是java.lang.Class的实例对象,官网称该对象为类的类类型. Class 类的实例表示正在运 ...

  2. java jni 结构体_JAVA 的JNI,传参为结构体问题: 我在网上找的资料://返回一个结构 public native DiskInfo getStruct();...

    Java代码:classDiskInfo{//名字publicStringname;//序列号publicintserial;}//返回一个结构publicnativeDiskInfogetStruc ...

  3. 用VB如读取内存地址

    代码如下: Private Declare Function CreateToolhelpSnapshot Lib "kernel32" Alias "CreateToo ...

  4. Java中如何实现结构体?如何对结构体进行排序?

    ***今天在做老师布置的编程题时,遇到了本题.感觉这道题如果用C++来解决的话,用结构体比较好吧!哈哈哈,可能还有其他的好方法,但我目前想到的是用结构体来解决比较合理.Java中有结构体吗?如果有,它 ...

  5. java查看内存地址_Java内存机制和内存地址

    问题一: String str1 = "abc"; String str2 = "abc"; System.out.println(str1==str2); / ...

  6. java 结构体_Java实现单链表的简单操作

    文章目录 前言 一.基本实现思路 二.代码实现 1.定义结点类2.定义链表类3.测试调用4.结果 总结 前言 用Java实现单链表的简单操作,阅读本文和上一篇文章体会Java中类与C++中结构体指针的 ...

  7. java 获取内存地址_Java 的多态在 JVM 里原来是这样的

    多态 面向对象的编程语言里,「多态」是一个至关重要的概念.我们常说,面向对象的本质,是方法与数据的绑定.那对于一个拥有继承关系的类之间,方法的绑定,是终是子类「重写」父类的方法,通过父类的引用指向子类 ...

  8. 【C 语言】结构体 ( 指针运算与指针内存操作 | 结构体成员偏移量计算 )

    文章目录 一.指针运算 与 指针内存操作 二.结构体偏移量计算 一.指针运算 与 指针内存操作 指针变量算术运算 ( 指针可以是任意值 ) : 指针 是一个变量 , 如果对指针进行 算术 / 逻辑 等 ...

  9. java cas是原子性的么_Java 并发编程:AQS 的原子性如何保证

    当我们研究AQS框架时(对于AQS不太熟知可以先阅读<什么是JDK内置并发框架AQS>,会发现AbstractQueuedSynchronizer这个类很多地方都使用了CAS操作.在并发实 ...

最新文章

  1. Rhel6-mpich2 hpc集群配置文档
  2. 2021年春季学期-信号与系统-第七次作业参考答案
  3. 荣发护肤护甲增强配方 Hair, Skin and Nails Plus 100 tablets
  4. activity 启动模式_Android世界:Activity的启动模式及其适用范围
  5. Python 的类其实是一个特殊的对象
  6. java定义final_Java中何时将方法声明为final的
  7. 博图wincc连接数据块_西门子博途WINCC 可通过创建画面模板提高编程效率
  8. Tomcat之Windows下安装
  9. elk服务重启_ELK开机启动 service文件内容
  10. 如何进行大数据入门的学习
  11. 基于STM32读取W25Q64(模拟SPI)
  12. matlab泰勒展开样例,MATLAB绘图样例
  13. 网页html教学反思,教学反思怎么写
  14. 【hdu2298】【三分】Toxophily
  15. photoshop基本的操作
  16. excel网页服务器端,Excel服务VI――用Excel Web Services创建应用程
  17. numpy计算移动平均值
  18. PTA实验7-1-1 简化的插入排序 (15分) 本题要求编写程序,将一个给定的整数插到原本有序的整数序列中,使结果序列仍然有序。
  19. 健康跑@长沙城(上)
  20. 默克尔树(Merkle Tree)总结

热门文章

  1. 阿里乌镇大动作:平头哥开源 MCU 设计平台!
  2. 硅谷程序员的回归能拯救印度“芯”吗?
  3. 蔡崇信与马云的 20 年
  4. 华为发布智能数据解决方案FusionData,重定义数据基础设施,释放数据价值
  5. 比 Java 更具争议的 PHP,处处留坑?
  6. 资深架构师谈云原生生态的基石Kubernetes
  7. 无人驾驶飞机来了!空难后波音的电动飞机你敢乘吗?
  8. 俄罗斯黑客入侵冬奥会系统,还甩锅给朝鲜?
  9. php扫描目录字典,Python如何实现敏感目录扫描 Python实现敏感目录扫描代码示例...
  10. Presto在滴滴的探索与实践