Sunday 2 April 2017

Multithreading (Synchronization - I)

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


1. What is synchronization and why we need it?


AnswerSynchronization is a way to control execution of threads in Java. Let's assume you have a task which you have assigned to say 10 threads. All these 10 threads simultaneously doing the task assigned by you. Now if you want a particular part of task should execute by one thread at a time and all other thread should wait for their turn.
In this case you need to synchronize that part of task. So Java will allow only one thread to go inside and execute. Once that thread finishes the task then Thread Scheduler will pick next one and so on.

So we can say that whatever code we write inside synchronized block get execute by only one thread at a time. 
 Example:

 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
41
42
43
44
import java.util.ArrayList;
import java.util.List;

public class ThreadExample {
 public static void main(String[] args) throws InterruptedException {
  MyTaks myTaks = new MyTaks();
  Thread thread1 = new Thread(myTaks,"Worker Thread-1");
  Thread thread2 = new Thread(myTaks,"Worker Thread-2");
  Thread thread3 = new Thread(myTaks,"Worker Thread-3");
  
  thread1.start();
  thread2.start();
  thread3.start();
  
  thread1.join();
  thread2.join();
  thread3.join();
  
  System.out.println("Finish Order::"+myTaks.finisherList.toString());
  
 }
}

class MyTaks implements Runnable{
 List<String> finisherList = new ArrayList<String>();
 
 public void run(){
  doSomeWork();
  updateListOnceDone();
 }
 
 private void doSomeWork(){
  try {
   Thread.sleep(2000); //To simulate some work
  } catch (InterruptedException e) {
   e.printStackTrace();
  } 
 }
 
 private synchronized void updateListOnceDone(){
  String threadName = Thread.currentThread().getName();
  finishList.add(threadName);
 }
}
In above code we have started three worker thread and each one of them is doing the task defined in MyTask class.
Assume doSomeWork() method is task assigned to each thread that they can execute in parallel. Now once they are done with their execution they need to call updateListOnceDone() method in order to update the finisherList, so that main thread can track who finished in which order.
We have made updateListOnceDone() method synchronized,why?
Because if you look at the code closely updating the finisherList is not an atomic operation. It's two step process first you need to read the list and then add the value. 
If we do not make method synchronized then It might possible that after reading the list thread can lost CPU without adding value to list and other thread who finished later might update the list which will result in inconsistent result. 
So we can say that by making updateListOnceDone() method synchronized, we make sure that finisherList can access by only one worker thread at a time.
Note: Please not that synchronization directly impact the performance of your application as thread need to wait for each other. We need to use synchronization very carefully as it comes with significant performance overhead.

2. What is difference between method level and block level synchronization?

AnswerThere are two ways we can make code synchronized:
1. Method level: In this case we synchronized whole method. 
2. Block level: In this case we synchronized only some portion of code inside method.
The advantage of block level synchronization over method level is that we are not blocking entire method(In real life method use to have hundreds of lines) so other thread can run that part in parallel.
We should always prefer block level synchronization over method level as block level synchronization has less performance overhead than method level()
Example: In below code we have added one more method updatedListOnceDoneBlockLevel() in which we have block level synchronization as only list update operation need to synchronized in our case.

 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
class MyTaks implements Runnable{
 List<String> finishList = new ArrayList<String>();
 
 public void run(){
  doSomeWork();
  updateListOnceDone();
 }
 
 private void doSomeWork(){
  try {
   Thread.sleep(2000); //To simulate some work
  } catch (InterruptedException e) {
   e.printStackTrace();
  } 
 }
 
 private synchronized void updateListOnceDone(){
  String threadName = Thread.currentThread().getName();
  finishList.add(threadName);
 }
 
 private void updateListOnceDoneBlockLevel(){
  String threadName = Thread.currentThread().getName();
  synchronized(this){
   finishList.add(threadName);
  }
 }
}

3. What is difference between synchronizing a static and a non static method?

Answer: Whenever a thread about to enter inside a synchronized method, It should first take lock on the object before going in. The thread takes the lock on method's object.
Every object in Java holds one single lock. So this is the reason once one thread takes that lock and go inside method no other thread can go as object doesn't has any lock to offer. After thread finishes the synchronized method it releases the lock back to object so lock can be acquire by other thread.
Now coming back to question in case of non static method (member method) thread takes the lock on object of class to which method belongs to but in case of static thread takes the lock on class level as static method belong to class.
Note: Java does create one object(Class type) for every class while loading classes. So basically in case of static method thread acquire the lock of that class object of Class type.
Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MyTaks implements Runnable{
 public void run(){
 }
 
 private synchronized void memberMethod1(){
  //Thread-1 is inside
 }
 
 //Thread-2 is arrived
 private synchronized void memberMethod2(){
   
 }
 
 //Thread-3 is arrived
 private static synchronized void staticmethod1(){
  
 }
 //Thread-4 is arrived
 private static synchronized void staticmethod2(){
  
 }
}
We have four method in above code two static and two member (non-static). Assume all threads are using same object of MyTask.
Assume Thread-1 is already inside memberMethod1(It means it already acquired the lock of object) and then three other thread arrived outside of each method. Now which one will go inside to their respective method and which will wait outside?
Let's start with Thread-2, Since It is trying to go inside a member(non-static) method hence it first need to acquire lock on object. Since lock on object already taken by Thread-1(since Thread-1 is already inside a memberMethod1())  hence Thread-2 need to wait.
Thread-3 and Thread-4 both are competing for static method. Only one of them go inside by acquiring the lock on class level(object of Class type) and other need to wait. Which one will go inside it all depends on Thread Scheduler.
Please note that Thread-1 has no impact on static methods as it is holding lock of object of MyTask.
Now assume Thread-1 completed it's execution and released the lock and meanwhile Thread-3 got inside staticMethod1(). Now can Thread-2 go inside memberMethod2()? Yes, It can surely go inside as object's(MyTask's object) lock is free. Please not that again Thread-3 has no impact on member methods as it has the lock of class level(object of Class type).
We can conclude that static method acquire the class level lock and non-static method acquire object level lock. 
Two Threads can execute one static and one non-static method respectively in parallel. But two non-static method of same object (or static method of same class) can not execute in parallel by two threads.

4. What is a lock? 

AnswerEvery object in Java has one single lock associated with it. This lock need to acquire by thread in order to access synchronized methods or block.
Whenever a thread about to enter synchronized method/block, It first need to check for lock, If object doesn't has lock(it means lock is already acquired by some other thread) then thread need to wait outside synchronized method/block for lock to become free.
Once lock is free thread will acquire it and will go inside synchronized method/block.

Note: Lock hold by Java objects are by default Re-entrant in nature. It means if a thread already holding a lock and it encountered another synchronized method/block of same object then thread can go inside.

5. Explain inter-thread communication?

Answer: There are situations where threads running inside an application need to communicate with each other in order to complete assigned task. For example assume some threads are waiting for a resource(e.g. lock of object) to become free which is already acquired by a thread. Now when that resource will be free then there should be some mechanism in place by which waiting threads will come to know that resource is available and they can access it now. So inter-thread communication helps threads to talk to each other.
Java has implemented wait and notify mechanism for inter-thread communication.
There are three methods involve in this mechanism of inter-thread communication. All three methods can only be called from inside the synchronized method/block.
1. wait(): wait() method is defined in Object class. Once a thread call the wait() method it releases the lock it has and then goes into waiting state. Once thread goes into waiting state then it can only wake up via notify()/notifyAll() method call on same object by any other thread.
2. notify(): notify() method is also defined in Object class. When a thread call notify() on a object then one of the thread out of all threads waiting for that object come out of waiting stage and goes into runnable stage. Now It's all depends on Thread Scheduler to pick and run that thread. 
3. notifyAll(): notifyAll() method is also defined in Object class. When a thread call notifyAll() on a object then all the threads waiting for that object come out of waiting stage and go into runnable stage. Now It's all depends on Thread Scheduler to pick and run that thread.
Difference between notify() and notifyAll() is notifyAll() wake up all the threads waiting for object on which notifyAll() called. But notify() wake up any one thread out of all the threads waiting for object on which notify() called.
Famous example of inter-thread communication is "Producer Consumer Problem". 

6. Discuss and implement Producer-Consumer problem?

AnswerIn this problem we have two two worker thread:1. Producer and 2. Consumer. We also have one shared resource.
Both Producer and Consumer will share one common resource. Producer will produce items and will put it into shared resource and Consumer will consume those items from the same shared resource.
Problem: it might possible Producer thread keep producing items without checking whether or not Consumer thread consumed those items which produced earlier.
and on the other hand it might possible Consumer will keep try to consume items from shared resource even their is no item to consume.
The above problem exist because their is no proper way of communication between Producer and Consumer. 
Solution: Producer should inform to Consumer to consume items once Producer have put the item in shared resource. Also Consumer should inform to Producer once it consumed the items from shared resource. 
It means Consumer will wait till Producer notify that item is produced. And Producer will wait till Consumer notify that item has consumed from shared resource.
Program(Problem):
 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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import java.util.ArrayList;
import java.util.List;

public class ProdConsProblem {
 public static void main(String[] args) {
  List<Integer> list = new ArrayList<>();

  Thread producerThread = new Thread(new Producer(list));
  Thread consumerThread = new Thread(new Consumer(list));

  producerThread.start();
  consumerThread.start();
 }
}

class Producer implements Runnable{
 List<Integer> list; 
 public Producer(List<Integer> list) {
  this.list = list;
 }
 
 @Override
 public void run() {
  while(true){
   produce();
  }
 }
 
 public void produce() {
  synchronized (list) {
   if (list.size() == 5) {
    System.out.println("List is Full Waiting to Produce.....");
   } else {
    list.add(list.size());
   }

  }
 }
}

class Consumer implements Runnable{
 List<Integer> list;
 public Consumer(List<Integer> list) {
  this.list = list;
 }
 
 @Override
 public void run() {
  while(true){
   consume();
  }
 }
 
 public void consume() {
  synchronized (list) {
   if (list.isEmpty()) {
    System.out.println("List is Empty Waiting to Consume.....");
   } else {
    System.out.print("Consumed::" + list.get(list.size() - 1));
    list.remove(list.size() - 1);
    System.out.println(" List::" + list.toString());
   }
  }
 }
}

Let's discuss code now we have two two worker thread : 1. producerThread and 2. consumerThread. We also have one shared resource and that is list(assume for now that list can hold only 5 items).
In problem code as you can see we have not used wait and notify, it means these two thread are not communicating with each other.
Hence if you run the above code you may find that producerThread is keep running (trying to add more item instead of giving chance to consumerThread) despite their is no room for new item in list. And you may also find that consumerThread is keep running (trying to get more item instead of giving chance to producerThread despite their is no more items in list.

In solution code below we have used wait and notify hence their is proper inter thread communication.
producerThread first check if list is full then it call wait() on list object instead of trying to add more which will eventually give chance to consumerThread to run.
If list is not full then producerThread will add item into list and then call notify() on list, it means it is sending signal to consumerThread that item is ready to consume in list.
consumerThread first check if list is empty then it call wait() on list object instead of trying to read item which will eventually give chance to producerThread to run.
If list is not empty then consumerThread will read item from list and then call notify() on list, it means it is sending signal to producerThread that item is consumed from list.

Program(Solution):
 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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import java.util.ArrayList;
import java.util.List;

public class ProdCons {
 public static void main(String[] args) {
  List<Integer> list = new ArrayList<>();

  Thread producerThread = new Thread(new Producer(list));
  Thread consumerThread = new Thread(new Consumer(list));

  producerThread.start();
  consumerThread.start();
 }
}

class Producer implements Runnable{
 List<Integer> list; 
 public Producer(List<Integer> list) {
  this.list = list;
 }
 
 @Override
 public void run() {
  while(true){
   produce();
  }
 }
 
 public void produce(){
  synchronized (list) {
   try {
    if(list.size() == 5){
     System.out.println("List is Full Waiting to Produce.....");
      list.wait();
     }
    else{
     list.add(list.size());
     System.out.print("Produced::"+(list.size() - 1)+" ");
     System.out.println("List::"+list.toString());
     list.notify();
     }
   } catch (InterruptedException e) {
    
   }
  }
 }
}

class Consumer implements Runnable{
 List<Integer> list;
 public Consumer(List<Integer> list) {
  this.list = list;
 }
 
 @Override
 public void run() {
  while(true){
   consume();
  }
 }
 
 public void consume(){
  synchronized (list) {
   try {
    if(list.isEmpty()){
     System.out.println("List is Empty Waiting to Consume.....");
     list.wait();
    }else{
     System.out.print("Consumed::"+list.get(list.size() - 1));
     list.remove(list.size() - 1);
     System.out.println(" List::"+list.toString());
     list.notify();
    }
   }catch (InterruptedException e) {
    
   }
   
  }
 }
}

Please check Multithreading (Synchronization - II) as well.

No comments:

Post a Comment