“嗨,阿米戈!”

“嗨,艾莉!”

“我想告诉你 volatile 修饰符。你知道那是什么吗?”

“跟线有关,记不太清了。”

“那就听好了。这里有一些技术细节供您参考:”

“计算机有两种内存:全局(普通)内存和处理器内置内存。处理器内置内存分为寄存器、一级缓存(L1)、二级缓存(L2)和第三级(L3)。”

“这些类型的内存具有不同的速度。最快和最小的内存是寄存器,然后是处理器缓存(L1、L2、L3),最后是全局内存(最慢)。”

“全局内存和处理器缓存以截然不同的速度运行,因此 Java 机器允许每个线程将最常用的变量存储在本地线程内存(在处理器缓存中)中。”

“这个过程能以某种方式被控制吗?”

“不完全是。所有工作都由 Java 机器完成。在优化性能方面非常智能。”

“但这就是为什么我要告诉你这个。有一个小问题。当两个线程使用同一个变量时,每个线程都可以在自己的本地缓存中存储一​​个副本。然后一个线程可能会更改变量,但第二个可能看不到变化,因为它仍在使用自己的变量副本。”

“嗯,那有什么办法呢?”

“Java的创造者为这种情况提供了一个特殊的关键字:volatile。如果一个变量被不同的线程访问,你需要用volatile修饰符来标记它,这样Java机器就不会把它放入缓存中。通常情况下是这样的看起来:“

public volatile int count = 0;

“哦,我想起来了,你已经说过了,我已经知道了。”

“你当然记得。但只有当我告诉你时你才记得。”

“呃,好吧,我有点忘记了。”

“重复是学习之母!”

“这里有几个关于volatile修饰符的新事实。volatile修饰符只保证变量被安全地读写,不保证它被安全地改变。”

“有什么不同?”

“看看变量是如何改变的:”

代码 真正发生了什么: 描述
count++
register = count;

register = register+1;

count = register;
步骤 1.
变量 count 的值从全局内存复制到处理器寄存器。

步骤 2.
在处理器内部,寄存器变量加 1。

第 3 步。
变量的值从处理器复制到全局内存。

“哇!这么说,所有变量都只在处理器中改变了?”

“是的。”

“这些值被来回复制:从内存到处理器再复制回来?”

“是的。”

“volatile 修饰符保证当变量 count 被访问时,它将从内存中读取(第 1 步)。如果一个线程想要分配一个新值,它肯定会在全局内存中(第 3 步)。”

“但 Java 机器不保证在步骤 1 和步骤 3 之间不会有任何线程切换。”

“那么,变量加1实际上是三个操作?”

“是的。”

“如果两个线程同时要执行 count++,那么它们会互相干扰吗?”

“是的,检查一下:”

线程 1 线程 2 结果
register1 = count;
register1++;
count = register1;
register2 = count;
register2++;
count = register2;
register1 = count;
register2 = count;
register2++;
count = register2;
register1++;
count = register1;

“所以,你可以访问变量,但改变它仍然有风险?”

“嗯,可以改的,小心点就好☺”

“如何?”

同步 是我们最好的朋友。”

“我懂了。”