考虑这个来自GeeksForGeeks的例子:
public class VolatileKeyword {
private static final Logger LOGGER = Logger.getLogger(VolatileKeyword.class);
private static int MY_INT = 0;
public static void main(String[] args) {
new ChangeListener().start();
new ChangeMaker().start();
}
static class ChangeListener extends Thread {
@Override public void run() {
int local_value = MY_INT;
while (local_value < 5) {
// (A)
if (local_value != MY_INT) {
LOGGER.info("Got Change for MY_INT : " + MY_INT);
local_value = MY_INT;
}
}
}
}
static class ChangeMaker extends Thread {
@Override public void run() {
int local_value = MY_INT;
while (MY_INT < 5) {
LOGGER.log(Level.INFO, "Incrementing MY_INT to " + (local_value + 1));
MY_INT = ++local_value;
try {
Thread.sleep(500);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
如本文所述,您看到的输出始终是:
Incrementing MY_INT to 1
Incrementing MY_INT to 2
Incrementing MY_INT to 3
Incrementing MY_INT to 4
Incrementing MY_INT to 5
这是由于ChangeListener
总是从其线程本地缓存中读取MY_INT
。(我测试了一下以确定)。
但是,如果您要用以下任何内容替换// (A)
:
Thread.currentThread().isAlive();
Thread.sleep(0)
Thread.interrupted()
System.out.print("");
- 将
volatile
整数x
添加到ChangeListener
,并将// (A)
替换为x += MY_INT
除其他事项外,您将获得与将MY_INT
设置为volatile
变量相同的输出。也就是说,
Incrementing MY_INT to 1
Got Change for MY_INT : 1
Incrementing MY_INT to 2
Got Change for MY_INT : 2
Incrementing MY_INT to 3
Got Change for MY_INT : 3
Incrementing MY_INT to 4
Got Change for MY_INT : 4
Incrementing MY_INT to 5
Got Change for MY_INT : 5
我上面列出的导致线程缓存被清除的操作的共同点是什么?
编辑:最后两个建议显然针对易失性或同步代码,导致"缓存重置",但其他建议呢?
我猜…这是一个竞争条件。这意味着当两个线程试图访问某个变量时,它将其缓存到以下几个操作中,以使其操作更快。为了避免这种情况,您可以使用AtomicInteger
类,当您检索基于内存中当前值的整数时,它是线程安全的。检查https://www.tutorialspoint.com/java_concurrency/concurrency_atomic_integer.htm