CodeGym /Courses /Java Core /The local cache: a multithreading problem. Volatile

The local cache: a multithreading problem. Volatile

Java Core
Level 7 , Lesson 5
Available

"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."

Comments (16)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Максим Василенко Level 44, Kiev, Ukraine
30 November 2022
if we have volatile List field and 2 or more threads will write new values in it ( but not just list.add(some value) but for example list.add(i, list.get(i) * 2). In this case volatile can't insure that everything will be alright. Do we have another way or only to do this field synchronized?
Ryan Vibbert Level 1, Columbus, United States
7 April 2021
Does each thread have its own cache? Why isnt this cache shared or how is it determined that it will use a different cache?
Gellert Varga Level 23, Szekesfehervar, Hungary
5 May 2021
http://tutorials.jenkov.com/java-concurrency/volatile.html
12 August 2020
I don't understand the example... Particularly this phrase:

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.
The variable isCancel is private non-static. How is it supposed to be shared by multiple threads? It's non static variable, which means each thread will have it's own instance. Should threads use static variable instead in the example? Or am I missing something?
Andrei Level 41
7 January 2021
Have you found an answer to this?
Gellert Varga Level 23, Szekesfehervar, Hungary
6 May 2021
I think: They didn't write it in the example, but they wanted to create a second thread this way:

   Thread clockThread2 = new Thread (clock);
So the second thread would work on the same clock object, = on the same instance variables. Accordingly: "The variable isCancel is a private non-static variable." - Yes. "Each thread will have it's own instance from this variable". - No! If two threads are working on the same object, then we have two threads but only one piece of nonstatic isCancel variable. Two little robots want to read the value of the same single one variable. (But if these two little robots call any instance-method of this single object, and this method has some local variables, then each robot will have own instances from these local variables.)
fifi deng Level 22, Paris, France
16 July 2020
why in the example,add volatile or not, the same ooutput
Sela Level 20, Poland
30 July 2020
1. only one Thread was started and variable's value was processed by one core. 2. by multiple Treads the processor may have only one core. 3. by single shot also may happen to process all the Threads in one core (especially small amount of Threads). 4. large amount of Threads could be started sequentially - one by one - not interfering each other so the value of the variable is written back to shared memory long before next Thread is started
Michael Brimage Level 19, Washington D.C., United States
4 May 2020
Level 17 is rocking my world right now!!
oli blaustrom Level 18, Luzern, Switzerland
5 March 2020
Would synchronized in this case also work instead of using volatile?
Tara Rosenthal Level 18, Farmers Branch, United States
12 May 2020
Synchronized does something totally different than volatile. Synchronized prevents multiple threads from running the same code block or method at the same time. Volatile ensures that a variable is always read from and written to slow memory rather than the cache (fast memory). So multiple threads can read or write a volatile variable at the same time but always to and from slow memory. If you need to prevent them writing the variable at the same time, then you would put the code block "variable = new value" in a synchronized block. While the variable instantiation would be: "volatile variable = initial value" to ensure that changes and references are always made to/from slow memory.
Tian Pro Level 23, Cape Town, South Africa
10 February 2020
It's gonna be hard to know when to use this "volatile" keyword and when not to...seems like one'll have to be somehow mentally mapping together the whole cache system while creating complicated programs, I dunno.
Pablo Souza Level 22, Brisbane, Australia
4 May 2020
The second exercise shows how the outcome of a thread isn't what's expected when the volatile isn't used.
28 August 2019
finally! the "volatile" keyword. What a fancy solution.
Ivaylo Trifonov Level 25, Madrid, Spain
1 August 2019
Every day a new interesting thing! Great!
Muhammad Vahhaaj Level 19, Rawalpindi, Pakistan
19 July 2019
wew