educative.io

What are "All the variables alongside volatile variable"

Please explain what are all the variables alongside the volatile variable that are being updated in main memory directly. Please refer to the following text:

If a variable is declared volatile then whenever a thread writes or reads to the volatile variable, the read and write always happen in the main memory. As a further guarantee, all the variables that are visible to the writing thread also get written-out to the main memory alongside the volatile variable. Similarly, all the variables visible to the reading thread alongside the volatile variable will have the latest values visible to the reading thread.

1 Like

Hi @Atul_Verma

   We will clarify the volatile keyword. When a variable is marked

as volatile, it means that every read of the variable will be read from the computer’s main memory and not from the CPU cache. And that every write to a volatile variable will be written to main memory and not just to the CPU cache. Why or when do we even use the volatile keyword? Let us understand the problem first. Assume a multithreaded application where the threads operate on standard variables. Here each thread may copy variables from the main memory into a CPU cache. While working on them for performance reasons.

If there is more than one CPU, each thread may run on a different CPU. That means that each thread may copy the variables into the CPU cache of different CPUs. With standard variables, there is no guarantee when the Java Virtual Machine reads data from main memory into CPU caches or writes data from CPU caches to main memory. Now imagine a situation where two threads try to access a shared counter variable that has been assigned 0.

Imagine that thread 1 increments the counter variable, but both thread 1 and thread 2 may read.

The counter variable frequently the problem with this standard counter variable is that it does not guarantee when the value is written from the CPU cache back to the main memory. So the value in the CPU cache may not be the same as in the main memory. Thus in a multithreaded environment, threads may not see the latest value of a variable because it has not yet been written back to the main memory by another thread. This is called a visibility problem. Here comes the role of the volatile keyword. The Java volatile keyword is intended to address variable visibility problems. If we declare the counter variable as volatile, all writes to the counter variable will be immediately written back to the main memory. Also, all reads of the counter variable will be read directly from the main memory. The volatile declaration of the counter variable looks like this:

public class MyClass{
public volatile int counter = 0;
}

Let’s see what happens in this case for the same scenario. So when one thread t1 modifies the counter and another thread t2 reads the counter but never changes it. Declaring the counter variable volatile is enough to guarantee
visibility for the second thread for writes to the counter variable.
If both t1 and t2 try to increment the counter variable, then declaring the counter variable volatile would not be enough to ensure that the second thread sees the latest written value. I mean as soon as a thread needs to first read the value of a volatile variable and generate the new value for the shared volatile variable based on that value. A volatile variable is no longer enough to guarantee correct visibility. This is because the short time gap between the reading of the volatile variable and its new value will create the race condition. Then multiple threats might read the same value of the volatile variable to generate the new value for the variable and then write the value back to main memory will overwrite each other’s values.

In this case, where multiple threads are present, a volatile variable is not.
If we try to understand this, thread 1 reads a shared counter variable with the value 0 into the CPU cache. Then it increments it to 1. thread 2 could then read the same counter variable for main memory where the variable’s value is still 0 into its own CPU cache. Threat 2 could then increment the counter to 1 and not write that back to the main memory. Thread 1 and thread two are now practically out of sync. because the real value of the The shared counter variable should have been 2, but here, each thread has the value 1 for the variable in their CPU caches. and in main memory the value is still 0 which is a mess.
in such a scenario we need to use synchronized keyword which will
guarantee that the reading and writing of the variable is atomic. reading or writing a volatile variable does not block threads reading or writing. so you must use the synchronized keyword around critical sections.
I assume you have followed me so fa.r full volatile visibility guarantee the
visibility guarantee of Java volatile goes beyond the volatile variable itself. If thread A writes to a volatile variable and thread B subsequently reads the same volatile variable than all variables visible to thread A before writing the volatile variable will also be visible to threat B. After it has read the volatile variable if thread A reads a volatile variable then all variables visible to thread A when reading the volatile variable will also be reread for main memory.

public class MyClass {
private int years;
private int months;
private volatile int days;
public void update(int years, int months, int days)
{
this.years = years;
this.months = months;
this.days = days;
}
}

In this update method it writes three variables of which only days is volatile. As per the full volatile visibility guarantee when a value is written today’s then all variables visible to the thread are also written to main memory. So here when a value is written today’s the values of years and months are also written to main memory. Now let us focus on instruction reordering. The Java Virtual Machine and the CPU are allowed to reorder instructions in the program for performance reasons. As long as the semantic meaning of the instructions remain the same for example:
int a = 1; int b = 2; a++; b++;
These instructions can be reordered, without changing the semantic meaning to:
int a = 1; a++; int b = 2; b++;

But when there is a volatile variable involved it poses a challenge. Let us study this example here closely look the update method once the update method writes a value of today’s the newly written values two years and months are also written to main memory:
public class MyClass {
private int years;
private int months;
private volatile int days;
public void update(int years, int months, int days)
{
this.years = years;
this.months = months;
this.days = days;
}
}

But, what if the Java VM reordered the instructions, like this:
public void update(int years, int months, int days)
{
this.days = days;
this.months = months;
this.years = years;
}

This time it happens before the new values have been written to months and years. The new values are thus not properly made visible to other threads.
To solve this issue Java guarantees these volatile happens before all reads or writes.
Before a write to a volatile variable, the reads or writes are guaranteed to “happen before” the write to the volatile variable. All reads from and writes to other variables cannot be reordered to occur before a read of a volatile variable. If reads or writes originally occurred after the read of the volatile variable.

While using the volatile keywords more often we must also consider the
performance. Because of our reading and writing a volatile variable causes of to be read or written to main memory. This is obviously more expensive compared to the CPU cache. So we need to make use of volatile when we really need to enforce the visibility of variables.