Consider this example from 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();
}
}
}
}
}
As stated in the article, the output you see consistently is:
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
Which is due to the fact that the ChangeListener
is always reading MY_INT
from its thread-local cache. (I tested this to be sure).
However, if you were to replace // (A)
with any of the following:
Thread.currentThread().isAlive();
Thread.sleep(0)
Thread.interrupted()
System.out.print("");
- adding a
volatile
integerx
toChangeListener
and replacing// (A)
withx += MY_INT
among other things, you would get the same output as you would by making MY_INT
a volatile
variable. That is,
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
What is is the common denominator with the operations I listed above that is causing the thread cache to clear?
EDIT: The last two suggestions clearly hit volatile or synchronized code leading to the "cache reset", but what about the others?