"Hello, Amigo! You remember that Ellie told you about the problems that arise when several threads try to simultaneously access a shared resource, yes?"

"Yes."

"The thing is, that's not all. There's another small problem."

As you know, a computer has memory where data and commands (code) are stored, as well as a processor that executes these commands and works with the data. The processor reads data from memory, changes it, and writes it back to memory. To speed up computations, the processor has its own built-in "fast" memory: the cache.

The processor runs faster by copying the most frequently used variables and areas of memory to its cache. Then it makes all the changes in this fast memory. And then it copies the data back to «slow» memory. All this while, the slow memory contains the old (unchanged!) variables.

This is where the problem arises. One thread changes a variable, such as isCancel or isInterrupted in the example above, but a second thread «doesn't see this change, because it happened in the fast memory. This is a consequence of the fact that the threads don't have access to each other's cache. (A processor often contains several independent cores and the threads can run on physically different cores.)

Let's recall yesterday's example:

Code Description
class Clock implements Runnable
{
private boolean isCancel = false;

public void cancel()
{
this.isCancel = true;
}

public void run()
{
while (!this.isCancel)
{
Thread.sleep(1000);
System.out.println("Tick");
}
}
}
The thread «doesn't know» that the other threads exist.

In the run method, the isCancel variable is put into the child thread's cache when it is used for the first time. This operation is equivalent to the following code:

public void run()
{
boolean isCancelCached = this.isCancel;
while (!isCancelCached)
{
Thread.sleep(1000);
System.out.println("Tick");
}
}

Calling the cancel method from another thread will change the value of isCancel in normal (slow) memory, but not in other threads' caches.

public static void main(String[] args)
{
Clock clock = new Clock();
Thread clockThread = new Thread(clock);
clockThread.start();

Thread.sleep(10000);
clock.cancel();
}

"Whoa! And did they come up with a beautiful solution for this too, like with synchronized?"

"You won't believe it!"

The first thought was to disable the cache, but this made programs run several times slower. Then a different solution emerged.

The volatile keyword was born. We put this keyword before a variable declaration to indicate that its value must not be put in the cache. More precisely, it wasn't that it couldn't be put into the cache, it was simply that it always had to be read from and written to the normal (slow) memory.

Here's how to fix our solution so everything works fine:

Code Description
class Clock implements Runnable
{
private volatile boolean isCancel = false;

public void cancel()
{
this.isCancel = true;
}

public void run()
{
while (!this.isCancel)
{
Thread.sleep(1000);
System.out.println("Tick");
}
}
}
The volatile modifier causes a variable to always be read from and written to normal memory shared by all threads.
public static void main(String[] args)
{
Clock clock = new Clock();
Thread clockThread = new Thread(clock);
clockThread.start();

Thread.sleep(10000);
clock.cancel();
}

"That's it?"

"That's it. Simple and beautiful."