“嗨,阿米戈!”
“嗨,艾莉!”
“我想告诉你 volatile 修饰符。你知道那是什么吗?”
“跟线有关,记不太清了。”
“那就听好了。这里有一些技术细节供您参考:”
“计算机有两种内存:全局(普通)内存和处理器内置内存。处理器内置内存分为寄存器、一级缓存(L1)、二级缓存(L2)和第三级(L3)。”
“这些类型的内存具有不同的速度。最快和最小的内存是寄存器,然后是处理器缓存(L1、L2、L3),最后是全局内存(最慢)。”
“全局内存和处理器缓存以截然不同的速度运行,因此 Java 机器允许每个线程将最常用的变量存储在本地线程内存(在处理器缓存中)中。”
“这个过程能以某种方式被控制吗?”
“不完全是。所有工作都由 Java 机器完成。在优化性能方面非常智能。”
“但这就是为什么我要告诉你这个。有一个小问题。当两个线程使用同一个变量时,每个线程都可以在自己的本地缓存中存储一个副本。然后一个线程可能会更改变量,但第二个可能看不到变化,因为它仍在使用自己的变量副本。”
“嗯,那有什么办法呢?”
“Java的创造者为这种情况提供了一个特殊的关键字:volatile。如果一个变量被不同的线程访问,你需要用volatile修饰符来标记它,这样Java机器就不会把它放入缓存中。通常情况下是这样的看起来:“
public volatile int count = 0;
“哦,我想起来了,你已经说过了,我已经知道了。”
“你当然记得。但只有当我告诉你时你才记得。”
“呃,好吧,我有点忘记了。”
“重复是学习之母!”
“这里有几个关于volatile修饰符的新事实。volatile修饰符只保证变量被安全地读写,不保证它被安全地改变。”
“有什么不同?”
“看看变量是如何改变的:”
代码 | 真正发生了什么: | 描述 |
---|---|---|
|
|
步骤 1. 变量 count 的值从全局内存复制到处理器寄存器。 步骤 2. 第 3 步。 |
“哇!这么说,所有变量都只在处理器中改变了?”
“是的。”
“这些值被来回复制:从内存到处理器再复制回来?”
“是的。”
“volatile 修饰符保证当变量 count 被访问时,它将从内存中读取(第 1 步)。如果一个线程想要分配一个新值,它肯定会在全局内存中(第 3 步)。”
“但 Java 机器不保证在步骤 1 和步骤 3 之间不会有任何线程切换。”
“那么,变量加1实际上是三个操作?”
“是的。”
“如果两个线程同时要执行 count++,那么它们会互相干扰吗?”
“是的,检查一下:”
线程 1 | 线程 2 | 结果 |
---|---|---|
|
|
|
“所以,你可以访问变量,但改变它仍然有风险?”
“嗯,可以改的,小心点就好☺”
“如何?”
“同步 是我们最好的朋友。”
“我懂了。”
GO TO FULL VERSION