专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Java中的volatile关键字

1. 操作系统多线程基础

我们的程序最终都会由CPU执行指令的方式实现,但是CPU的处理速度远远大于内存的读写速度,所以CPU通过CPU(假设每个CPU都有自己的高速缓存,CPU之间的高速缓存相隔离)周围的高速缓存与主内存进行数据传输,高速缓存的速度是远高于主内存的,但是容量很小。

在CPU与主内存之间添加了高速缓存后,虽然解决了CPU与主内存的速度差异问题,但是也引入了另一个严重的问题:在多核CPU系统中,CPU会先读写高速缓存上的数据,并由高速缓存再与主内存进行传输,这就可能造成不同CPU之间的高速缓存上的数据不同,引起了数据的不一致性,也即多线程工作时不能保证数据的同步。下面举一个例子说明:

i = i+1;

如上代码所示,假设CPU1、CPU2同时开启线程1、2去执行这段代码,CPU1的高速缓存去主内存上load变量i的值,将i的值保存在高速缓存中,再传输给CPU1去执行,这时CPU1执行后使i赋值为1,再由CPU1传输给高速缓存,再写入主内存中。但是如果在这个过程中(主内存的变量还未更改)CPU2代表的第二个线程执行同样的程序时最终写入主内存的值也为“1”,与我们想要得到“i=2”的结果就不同了。

这样就引出了这个问题:缓存一致性问题,即变量在多个高速缓存中都有副本,但是这些副本之间可能不同。 为了解决缓存不一致性的问题,主要有两个方法:

    1. 在总线上加锁LOCK:在某一CPU工作期间,其他CPU无法通过总线读写主内存,这种方法下频率较低;
    1. 缓存一致性协议:

所有内存的传输都发生在一条共享的总线上,而所有的处理器都能看到这条总线:缓存本身是独立的,但是内存是共享资源,所有的内存访问都要经过仲裁(同一个指令周期中,只有一个CPU缓存可以读写内存)。

缓存一致性协议中最有名的当属MESI,MESI协议保证了每个缓存中使用的共享变量的副本是一致的。

它核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。

2. JVM内存模型

Java内存模型是Java虚拟机试图屏蔽不同硬件和操作平台的差异,让Java程序再各种平台下都能达到一致的效果。Java内存模型定义了程序中各个变量的访问机制,这里的变脸是指实例变量、静态字段、数组对象的元素等,但是不包括局部变量和方法形参,因为后者是线程私有的,不需要共享。

Java内存模型规定了所有的变量都保存在主内存中,每条线程都拥有自己的工作内存,可以类比CPU的高速缓存。工作内存中保存了主内存中的变量副本,线程对变量的读取、赋值都必须再工作内存中进行,不能直接操作主内存中的变量。

3. volatile关键字

被volatile修饰的变量具有以下特点:

1、 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2、 禁止进行指令重排序。

在具体的java中有以下作用:

1、 使用volatile关键字会强制将修改的值立即写入主存;
2、 使用volatile关键字的话,当线程2进行写入时,会导致线程1的工作内存中缓存变量的缓存行无效(反映到硬件层的话,就是CPU的L1或者L2缓存中对应的缓存行无效);
3、 由于线程1的工作内存中缓存变量的缓存行无效,所以线程1再次读取变量的值时会去主存读取。

文章永久链接:https://tech.souyunku.com/42547

未经允许不得转载:搜云库技术团队 » Java中的volatile关键字

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们