Tuesday 4 April 2017

Multithreading (Synchronization - II)

Synchronization concept plays very important role in multithreading  programming. Good knowledge of synchronization is must in order to write a code which shroud always produce consistent result. Synchronization is very famous in java interviews and strong knowledge of synchronization help you to crack interview.


1. What is race condition and how to solve it?

AnswerRace condition as name suggests is a scenario in which threads are compete with each other in order to get the CPU time and their task done, So It's kind of a virtual race between threads to finish their execution. Race condition leads to execution of codes in parallel which are suppose to execute sequentially. Race condition leads to inconsistent results. 

Below program explains race condition:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class RaceCondition {

 public static void main(String[] args) throws InterruptedException {
   IncrementTask incrementTask = new IncrementTask();
   Thread thread1 = new Thread(incrementTask);
   Thread thread2 = new Thread(incrementTask);
   Thread thread3 = new Thread(incrementTask);
   
   System.out.println("Value of counter before::"+incrementTask.getCounter());
   
   thread1.start();
   thread2.start();
   thread3.start();
   
   
   thread1.join();
   thread2.join();
   thread3.join();
   
   System.out.println("Value of counter after::"+incrementTask.getCounter());
 }

}

class IncrementTask implements Runnable{
 private Integer counter = 0;
 
 @Override
 public void run() {
  counter = counter + 1;
 }
 
 public Integer getCounter() {
  return counter;
 }
 public void setCounter(Integer counter) {
  this.counter = counter;
 }
 
}
If you run above code multiple times then in some cases you will get value of counter as 2 or 1 instead of 3(desired result).
We have started three threads and have assigned simple task where they need to increment value of counter variable by 1. As we have three threads and each thread will increment counter by 1 So the correct result will be 3.
But in some cases we will get 2 or 1. Why?
Answer is Race condition. Increment operation of counter variable look likes one step process but at CPU level it is three steps process:
1. Read the value of counter.
2. Increment the value by 1.
3. Write the new value to counter.
It might possible thread can loss CPU between these operations.
Now assume thread1 read the value of counter as 0, then it increment the value to 1. But before writing the value back to counter thread1 lost the CPU. 
Now assume thread2 got the chance. For thread2 value of counter is still 0 not 1 because thread1 didn't write yet. Let's assume thread2 successfully executed all three steps at once hence new value of counter will be 1. Assume thread1 got chance to execute again since it has already executed first two steps hence thread1 will directly write value of counter it has already stored in it's local cache and that is 1. Finally thread3 got chance and it will read value of counter as 1 and assume it also executed successfully all three steps at once hence new value of counter will be 2.
So we can say that since most of the task that we perform in Java generally takes more than one CPU operations (cycles) to execute and there is fair amount of chances that CPU can switch thread between these operations which leads to inconsistent results.
Solution: To guard your code against race condition you should put your code in synchronized method/block.
In above example if we put line number 30 inside synchronized block then only one thread can go inside i.e increment operation of counter variable will execute by only one thread at a time.
Also as per Java rule(for synchronization) every thread has to read from main memory(shared by all thread) before going inside synchronized block and has to flush value to main memory while exiting from synchronized block. This feature of synchronized block know as memory barrier.
Now every thread will see latest data(from main memory) instead of their local cache data.
Now let's recall above mentioned scenario to check how can synchronization fix the issue: As we saw thread1 executed 2 steps and then lost CPU and when thread2 got chance to execute it got the value of counter as 0 because thread1 didn't flush the data to main memory. Now synchronized block will force thread1 to flush cached value to main memory before exiting. Now thread2 will get value of counter as 1.

Other than synchronized block their are other solution as well like we for this particular scenario and for many more can use AtomicInteger available in Java concurrent API.
I have discussed AtomicInteger and other important features of Java concurrent API here.

2. What is use of volatile variable?

AnswerEach thread in your application now days runs in different cores of CPU. Each core of CPU has their own local cache. In simple word we can say that every thread got one local cache which is non-shareable type of memory.
Consider below diagram:
Fig: Volatile variable.


Thread-1 has read the value of foo as 1 from main memory, and added 1 into it now the value of foo is 2 and stored it in local cache. Till now Thread-2 and Thread-3 can see value of foo as 1(from main memory). Now if Thread-1 lost the CPU before it could write value back to Main memory then it will create inconsistency as for Thread-2 and Thread-3 value of foo is still 1.
To solve this issue we can mark foo as volatile: Volatile variables force JVM to execute all the CPU operation in one go. So if we mark foo as volatile JVM will always read value from main memory and will flush it to main memory after modifications(all CPU operation in one go). 
In some scenario we can use volatile variables as replacement of synchronized block. Even in above code we can fix the race condition by declaring counter variable as volatile instead of using synchronized block.
Volatile variables gives you guarantee that all thread will read value always from main memory and write it to main memory too.
Please note that volatile is not alternative of synchronized block but in some cases like above we can use volatile variables. But for large block of code we should prefer synchronized block/method.

No comments:

Post a Comment