When a shared variable is accessed by multiple threads , think about volatile keyword . When a field is accessed by multiple threads , one thread’s changes to the field should be visible to another thread. How it is possible ?. The following tutorial covers about
Some of the following reasons exist for why the changes of a field made by one thread is not communicated to another thread immediately (i.e any thread does not see the most up-to-date value of the field at any time)
:
a) Reordering of statements : Compiler optimizations are very important on modern platforms. Instructions are reordered by the compiler to achieve maximum performance . Due to this , instructions are frequently not executed in the order it was written. At the same time , system needs to maintain as-if-serial semantics for individual threads. This works well in a single-threaded environment. i.e when a single thread executes, the result will appear as if all of the actions performed by that thread occur in the order they appear in the original program. But when multiple thread involves , the statement reordering done for a single thread may be out of order for other threads which causes surprising results. Similarly Processor and caches are allowed to reorder.
b) Thread may keep values in local registers instead of shared memory whenever possible . So threads may see different values for shared variables.
c) On multiprocessor architectures, each processor may have own local caches to store shared variables that are out of sync with main memory .
One solution to the above problem is Synchronization . Synchronization ensures both
1. Visibility . (any thread sees the most up-to-date value of the field at any time)
2. Mutual exclusion (prevents methods / block of statements from being executed concurrently) . For more on synchronization , you can visit my earlier tutorial , Synchronization in java
Another solution will be volatile modifier It ensures the visibilty of a field between threads i.e any thread that reads the variable will see the last written value and it does not perform mutual exclusion ie. it allows methods / block of codes to be executed simultaneously by two or more threads.
Some of the examples given below will give you clear idea about when to use volatile
class Reorder1 {
static int value = 0;
static boolean flag;
static void one() {
value = 100;
flag = true;
}
static void two() {
if ( flag == true )
System.out.println("Value " + value);
}
In the above code , compiler is free to reorder of statements as given below as statements value = 100; , flag = true; have no dependency.
static void one() {
flag = true;
value = 100;
}
When the above code is executed by a single thread , calling one() & two() methods will give the output 100. When multiple threads execute the above methods simultaneously , the result may go wrong. When one thread execute the line flag=true , and another thread executes the line
if ( flag == true )
System.out.println("Value " + value);
Now the result will be 0 . One solution is to synchronize the above methods . It prevents method one and method two from being executed concurrently and also prevents reordering. Another solution is to declare the field flag as volatile . Volatile read and write operations can not be reordered with each other or with respect to nonvolatile variable accesses
public static PrimaryKeyGenerator getInstance()
{
if (instance == null)
{
synchronized(PrimaryKeyGenerator.class)
{
if (instance == null)
{
instance = new PrimaryKeyGenerator();
}
}
}
return instance; }
The above code is called double-checked locking idiom which implements singleton pattern suitable for multithreaded environment and also supports lazy initialization by avoiding the overhead of synchronization. But the above code does not work as expected due to the reordering of the initialization of instance and the write to the instance field by the compiler . Suppose two threads execute the above code simultaneously . The first thread writes to instance variable first then the object is constructed. In the meantime , the second thread reads the instance field which is not null before the first thread finishes the construction of object. So the second thread returns the incomplete object of PrimaryKeyGenerator . We can fix this problem with Double-Checked Locking by declaring the instance field volatile, which prevents reordering.
class WorkerOwnerThread
{
// field is accessed by multiple threads.
private static boolean stopSignal;
private static void doWork()
{
while (!stopSignal)
{
Thread t = Thread.currentThread();
System.out.println(t.getName()+ ": I will work until i get STOP signal from my Owner...");
}
System.out.println("I got Stop signal . I stop my work");
}
private static void stopWork()
{
stopSignal = true;
Thread t = Thread.currentThread();
System.out.println("Stop signal from " + t.getName() );
}
public static void main(String[] args) throws InterruptedException {
Thread workerThread = new Thread(new Runnable() {
public void run() {
doWork(); }
});
workerThread.setName("Worker");
workerThread.start();
//Main thread
Thread.sleep(100);
stopWork();
}
}
The above code is another example for when to use volatile modifier. Two threads Worker thread and Main thread are involved. Worker thread starts and running continousely until it gets stop signal from the main thread. This situation is a common use case for volatile.
Under the new memory model (from JLS7), when one thread writes to a volatile variable V, and thread B reads from V, any variable values that were visible to A at the time that V was written are guaranteed now to be visible to B. Here stopSignal is volatile variable. The changes made by the main thread to the stopSignal variable is communicated to Worker thread immediately.
In the above code , if we synchronize both methods doWork() and stopWork() instead of using volatile variable , if the worker thread starts first , then it never stops . As synchronization performs mutual exclusion , when the worker thread starts first , the main thread never gets chance to update stopSignal = true. So declaring the stopSignal field as volatile is the good solution.
1. The reads and writes of volatile fields is made directly to main memory, instead of to registers or the local processor cache. This ensures that all the threads see the the most recent written value of the field at all times
2. Fields that are declared volatile are not subject to compiler optimizations. ie. the reads and writes of volatile variables can not be reordered with each other or with respect to nonvolatile variable accesses. A write to a volatile field happens-before every subsequent read of that same volatile.
1. The volatile modifier is applied to a field that is accessed by multiple threads without using the lock
2. Writes and reads of long and double variables declared as volatile are always atomic
2. The volatile keyword can not be applied to local variable
3. final variable can not be a volatile.
4. volatile modifier performs no mutual exclusion
5. Object variable declared as volatile can be null .
6. Reads and writes operations of volatile variables can not be reordered with each other respect to each other or with respect to nonvolatile variable accesses
7. A write to a volatile field happens before every subsequent read of that same volatile
Note : volatile is not the alternative to synchronization .
You can visit my another post for Difference between volatile and synchronized keyword
1. What volatile keyword indicates ?
2. Why volatile keyword is required?
3. When to use volatile modifier?
4. How volatile works in java? .
1. What volatile keyword indicates ?
The volatile keyword indicates that a field can be be modified by multiple threads that are executing simultaneously. If a field is declared as volatile, then the Java Memory Model (JMM 133 , from java 5 ) ensures that all threads see a consistent value for the variable.2. Why volatile keyword is required?
Some of the following reasons exist for why the changes of a field made by one thread is not communicated to another thread immediately (i.e any thread does not see the most up-to-date value of the field at any time)
:
a) Reordering of statements : Compiler optimizations are very important on modern platforms. Instructions are reordered by the compiler to achieve maximum performance . Due to this , instructions are frequently not executed in the order it was written. At the same time , system needs to maintain as-if-serial semantics for individual threads. This works well in a single-threaded environment. i.e when a single thread executes, the result will appear as if all of the actions performed by that thread occur in the order they appear in the original program. But when multiple thread involves , the statement reordering done for a single thread may be out of order for other threads which causes surprising results. Similarly Processor and caches are allowed to reorder.
b) Thread may keep values in local registers instead of shared memory whenever possible . So threads may see different values for shared variables.
c) On multiprocessor architectures, each processor may have own local caches to store shared variables that are out of sync with main memory .
One solution to the above problem is Synchronization . Synchronization ensures both
1. Visibility . (any thread sees the most up-to-date value of the field at any time)
2. Mutual exclusion (prevents methods / block of statements from being executed concurrently) . For more on synchronization , you can visit my earlier tutorial , Synchronization in java
Another solution will be volatile modifier It ensures the visibilty of a field between threads i.e any thread that reads the variable will see the last written value and it does not perform mutual exclusion ie. it allows methods / block of codes to be executed simultaneously by two or more threads.
3. When to use volatile modifier
Some of the examples given below will give you clear idea about when to use volatile
Example 1
class Reorder1 {
static int value = 0;
static boolean flag;
static void one() {
value = 100;
flag = true;
}
static void two() {
if ( flag == true )
System.out.println("Value " + value);
}
In the above code , compiler is free to reorder of statements as given below as statements value = 100; , flag = true; have no dependency.
static void one() {
flag = true;
value = 100;
}
When the above code is executed by a single thread , calling one() & two() methods will give the output 100. When multiple threads execute the above methods simultaneously , the result may go wrong. When one thread execute the line flag=true , and another thread executes the line
if ( flag == true )
System.out.println("Value " + value);
Now the result will be 0 . One solution is to synchronize the above methods . It prevents method one and method two from being executed concurrently and also prevents reordering. Another solution is to declare the field flag as volatile . Volatile read and write operations can not be reordered with each other or with respect to nonvolatile variable accesses
Example 2
public static PrimaryKeyGenerator getInstance()
{
if (instance == null)
{
synchronized(PrimaryKeyGenerator.class)
{
if (instance == null)
{
instance = new PrimaryKeyGenerator();
}
}
}
return instance; }
The above code is called double-checked locking idiom which implements singleton pattern suitable for multithreaded environment and also supports lazy initialization by avoiding the overhead of synchronization. But the above code does not work as expected due to the reordering of the initialization of instance and the write to the instance field by the compiler . Suppose two threads execute the above code simultaneously . The first thread writes to instance variable first then the object is constructed. In the meantime , the second thread reads the instance field which is not null before the first thread finishes the construction of object. So the second thread returns the incomplete object of PrimaryKeyGenerator . We can fix this problem with Double-Checked Locking by declaring the instance field volatile, which prevents reordering.
Example 3
class WorkerOwnerThread
{
// field is accessed by multiple threads.
private static boolean stopSignal;
private static void doWork()
{
while (!stopSignal)
{
Thread t = Thread.currentThread();
System.out.println(t.getName()+ ": I will work until i get STOP signal from my Owner...");
}
System.out.println("I got Stop signal . I stop my work");
}
private static void stopWork()
{
stopSignal = true;
Thread t = Thread.currentThread();
System.out.println("Stop signal from " + t.getName() );
}
public static void main(String[] args) throws InterruptedException {
Thread workerThread = new Thread(new Runnable() {
public void run() {
doWork(); }
});
workerThread.setName("Worker");
workerThread.start();
//Main thread
Thread.sleep(100);
stopWork();
}
}
The above code is another example for when to use volatile modifier. Two threads Worker thread and Main thread are involved. Worker thread starts and running continousely until it gets stop signal from the main thread. This situation is a common use case for volatile.
Under the new memory model (from JLS7), when one thread writes to a volatile variable V, and thread B reads from V, any variable values that were visible to A at the time that V was written are guaranteed now to be visible to B. Here stopSignal is volatile variable. The changes made by the main thread to the stopSignal variable is communicated to Worker thread immediately.
In the above code , if we synchronize both methods doWork() and stopWork() instead of using volatile variable , if the worker thread starts first , then it never stops . As synchronization performs mutual exclusion , when the worker thread starts first , the main thread never gets chance to update stopSignal = true. So declaring the stopSignal field as volatile is the good solution.
4. How volatile works in java?
1. The reads and writes of volatile fields is made directly to main memory, instead of to registers or the local processor cache. This ensures that all the threads see the the most recent written value of the field at all times
2. Fields that are declared volatile are not subject to compiler optimizations. ie. the reads and writes of volatile variables can not be reordered with each other or with respect to nonvolatile variable accesses. A write to a volatile field happens-before every subsequent read of that same volatile.
Some of the key points about volatile keyword
1. The volatile modifier is applied to a field that is accessed by multiple threads without using the lock
2. Writes and reads of long and double variables declared as volatile are always atomic
2. The volatile keyword can not be applied to local variable
3. final variable can not be a volatile.
4. volatile modifier performs no mutual exclusion
5. Object variable declared as volatile can be null .
6. Reads and writes operations of volatile variables can not be reordered with each other respect to each other or with respect to nonvolatile variable accesses
7. A write to a volatile field happens before every subsequent read of that same volatile
Note : volatile is not the alternative to synchronization .
You can visit my another post for Difference between volatile and synchronized keyword
No comments:
Post a Comment