Synchronization in Multithreading

<< Previous Chapter Next Chapter >>
Synchronization (Multithreading) in Java:

When all threads are sharing some common resource, then there might be possibility of data loss, overwrite etc. when one object is shared between multiple threads, in such case there should be a process which ensures that when one thread is accessing a shared resource then another thread should not be allowed to access it. This process is known as synchronization.

In java, every object holds an implicit lock which is called Monitor.




This situation occurs in web based and distributed applications.

package dev21century.threading;

public class Thread1 {
       int x, y, result;

       int add(int a, int b)
       {
              x = a;
              y = b;
              result = x + y;
              return result;
       }
}

Thread1 calls above method with 10, 20
Thread2 calls above method with 30, 40
Thread3 calls above method with 50, 60

There are 2 ways of implementing synchronization – one is to synchronize whole method and another is to synchronize a particular block.
Example
synchronized int add(int a, int b) {  }
Or
Synchronized {
//block code
}

Rule – if a thread is entering in a synchronized method or a synchronized block for a particular object then that thread must have the monitor (lock) of that object. So, when thread1 starts, it takes the lock first and then run, and if by chance while executing it went to the blocked state then it will take the lock with it. Now, when another thread enters with same object then obviously it cannot acquire the lock of that object and will not able to run and will wait until the first thread is completed to take the lock of the object.
So, this way we can stop data corruption.

Example:
Create a file RunSync.java in dev21century.threading package:
package dev21century.threading;

class Shared
{
  int x;
  synchronized void show(String s, int a)
  {
     x = a;
     System.out.println("Entered in method " + s + " " + x);
     try{
         Thread.sleep(2000);
        }
     catch(Exception e)
     {
     System.out.println(e);
     }
     System.out.println("Exit from method " + s + " " + x);
  }
}

class CustomThread1 extends Thread
{
       Shared s;
       CustomThread1(Shared s, String str)
       {
              super(str);
              this.s = s;
              start();
       }
       public void run()
       {
              s.show(Thread.currentThread().getName(), 10);
       }
}

class CustomThread2 extends Thread
{
       Shared s;
       CustomThread2(Shared s, String str)
       {
              super(str);
              this.s = s;
              start();
       }
       public void run()
       {
              s.show(Thread.currentThread().getName(), 20);
       }
}

class CustomThread3 extends Thread
{
       Shared s;
       CustomThread3(Shared s, String str)
       {
              super(str);
              this.s = s;
              start();
       }
       public void run()
       {
              s.show(Thread.currentThread().getName(), 30);
       }
}

public class RunSync {

   public static void main(String[] args) {
      Shared st = new Shared();
      new CustomThread1(st, "one");
      new CustomThread2(st, "two");
      new CustomThread3(st, "three");
    }
}

Output:


In above example you will notice that each "Entered in..." message will be printed in a gap of 2 seconds. This is because when one thread enters the show method it will first take the lock of shared object, will print message and sleep for 2 seconds, and as the show method is synchronized, that means no other thread can enter this method with same object for at least 2 seconds that’s why it takes 2 seconds for next message.

Now, just remove synchronized keyword and try once again, this time all 3 messages will be printed without waiting. So this is the beauty of synchronization.

Quick Question – if we have 2 methods synchronized then will the second thread call the second thread?
Answer – it will never allow calling second method as well because lock is at object level, and same object is getting passed for both methods (if we will remove “synchronized” then second method will be invoked.

Rule – the implicit lock (monitor) is also maintained at class level. In case of non static methods, the object lock is used, but in case of static methods the class level lock is used.

If one method is called through class and another through Object, then the lock will always be checked for class if using static method.

If one method is static and another is non static then both will run separately with class and object level locks.

Synchronized block:
In java we can make a block as synchronized instead of synchronizing complete method. This is beneficial because synchronizing a method may degrade the performance because, all statements that are not participating in multithreading will also be locked.

Syntax:
public void show()
{
synchronized (this)
{
            //code which needs synchronization
}

//code which does not need synchronization
}

In case of synchronized method, the lock is always acquired on current object, but in case of synchronized block the lock can be acquired on any object.

synchronized block is also used to make the object of any class synchronized, by default objects are not synchronized, but we can synchronize it by using synchronized block.

lets modify our existing program as follows:

package dev21century.threading;

class Shared
{
 int x;
 void show(String s, int a)
 {
  System.out.println("Entered in method " + s);
  synchronized(this)
  {
   x = a;
   System.out.println("Entered in synchronized block " + s + " " + x);
      
   try {
       Thread.sleep(2000);
       }
   catch (InterruptedException e) {
        System.out.println(e);
   }
   System.out.println("Exit from synchronized block " + s + " " + x);
      
  }
 }
}

class CustomThread1 extends Thread
{
       Shared s;
       CustomThread1(Shared s, String str)
       {
              super(str);
              this.s = s;
              start();
       }
       public void run()
       {
              s.show(Thread.currentThread().getName(), 10);
       }
}

class CustomThread2 extends Thread
{
       Shared s;
       CustomThread2(Shared s, String str)
       {
              super(str);
              this.s = s;
              start();
       }
       public void run()
       {
              s.show(Thread.currentThread().getName(), 20);
       }
}

class CustomThread3 extends Thread
{
       Shared s;
       CustomThread3(Shared s, String str)
       {
              super(str);
              this.s = s;
              start();
       }
       public void run()
       {
              s.show(Thread.currentThread().getName(), 30);
       }
}

public class RunSync {
 public static void main(String[] args) {
  Shared st = new Shared();
  new CustomThread1(st, "one");
  new CustomThread2(st, "two");
  new CustomThread3(st, "three");
 }
}

Output:

Now, in this example you will notice that all “Entered in method” messages will be printed without any wait, but “Entered in synchronized block…” messages will be printed with 2 seconds gap because they are in synchronized block, and without printing “Exit..” message the next “Entered in sync block..” message is not getting displayed. It means no threads are allowed in synchronized block unless the current thread is completed properly.


Now, lets understand how we can achieve this noncurrent:
In above example we have synchronized the current object, but many times in development we face the situation where we need to synchronize other objects as well. So lets modify our program as follows, here we have created a new class called Temp and tried to synchronize it from outside:

package dev21century.threading;
class Temp
{
     void display(String s)
     {
           System.out.println("Entered in display Method.." + s);
           try {
                Thread.sleep(2000);
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
           System.out.println("Exit from display method.." + s);
     }
}

class Shared
{
  int x;
  Temp temp = new Temp();
  void show(String s, int a)
  {
       System.out.println("Entered in show method " + s);
       synchronized(temp)
       {
        temp.display(s);
        try {
              Thread.sleep(2000);
        } catch (InterruptedException e) {
              System.out.println("Exit from synchronized block " + s + " " + x);
        }
       
       }
  }
}

class CustomThread1 extends Thread
{
       Shared s;
       CustomThread1(Shared s, String str)
       {
              super(str);
              this.s = s;
              start();
       }
       public void run()
       {
              s.show(Thread.currentThread().getName(), 10);
       }
}

class CustomThread2 extends Thread
{
       Shared s;
       CustomThread2(Shared s, String str)
       {
              super(str);
              this.s = s;
              start();
       }
       public void run()
       {
              s.show(Thread.currentThread().getName(), 20);
       }
}

class CustomThread3 extends Thread
{
       Shared s;
       CustomThread3(Shared s, String str)
       {
              super(str);
              this.s = s;
              start();
       }
       public void run()
       {
              s.show(Thread.currentThread().getName(), 30);
       }
}

public class RunSync {

   public static void main(String[] args) {
      Shared st = new Shared();
      new CustomThread1(st, "one");
      new CustomThread2(st, "two");
      new CustomThread3(st, "three");
    }
}

Output:

Quick Question – why suspend() and resume() are deprecated?
Answer – the suspend() method puts the thread in blocked pool infinitely and resume() brings it back. The problem is when a thread is suspended by suspend() method, it will take the lock with it, and in case if no one calls the resume() method then another thread can never enter that block. It will be a dead lock situation.

The alternate of suspend() and resume() is wait() and notify() both these methods belongs to Object class. The wait() method will acquire the lock and put thread in blocked pool.

notify() method will pull the thread from blocked pool which was blocked by wait() method.

Lets take this example:

package dev21century.threading;

class Shared
{
     int flag = 0;
     int data = 0;
     synchronized public void submit()
     {
           flag = 1;
           try {
                Thread.sleep(1000);
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
           data = 10;
           System.out.println("value submitted");
           notify();
     }
     synchronized int withdraw()
     {
           if(flag == 0)
           {
                try {
                     System.out.println("wait block");
                     wait();
                } catch (InterruptedException e) {
                     e.printStackTrace();
                }
           }
           return (data);
     }
    
}

class Thread1 extends Thread
{
     Shared s;
     Thread1(Shared s, String str)
     {
           super(str);
           this.s=s;
           start();
     }
     public void run()
     {
           System.out.println("data = " + s.withdraw());
     }
}

class Thread2 extends Thread
{
     Shared s;
     Thread2(Shared s, String str)
     {
           super(str);
           this.s=s;
           start();
     }
     public void run()
     {
           //System.out.println("data = " + s.withdraw());
           s.submit();
     }
}

public class RunSync {

   public static void main(String[] args) {
       
        Shared sharedObj = new Shared();
        new Thread1(sharedObj, "one");
        new Thread2(sharedObj, "two");
    }
}

Output:



There is another deadlock condition which might occur, so we should always avoid such kinds of programming, lets see this example:

package dev21century.threading;
class Temp
{
     void display(String s)
     {
           System.out.println("Entered in display Method.." + s);
           System.out.println("Exit from display method.." + s);
     }
}

class CustomThread1 extends Thread
{
       Temp t1, t2;
       CustomThread1(Temp t1, Temp t2, String str)
       {
              super(str);
              this.t1 = t1;
              this.t2 = t2;
              start();
       }
       public void run()
       {
       synchronized(t1)
       {
              try {
                     Thread.sleep(2000);
                } catch (InterruptedException e) {
                     e.printStackTrace();
                }
                t1.display(Thread.currentThread().getName());
                synchronized(t2)
                {
                     t2.display(Thread.currentThread().getName());
                }

       }
       }
}

class CustomThread2 extends Thread
{
       Temp t1, t2;
       CustomThread2(Temp t1,Temp t2, String str)
       {
              super(str);
              this.t1 = t1;
              this.t2 = t2;
              start();
       }
       public void run()
       {
       synchronized(t2)
       {
              try {
                     Thread.sleep(1000);
                } catch (InterruptedException e) {
                     e.printStackTrace();
                }
              t2.display(Thread.currentThread().getName());
                synchronized(t1)
                {
                     t1.display(Thread.currentThread().getName());
                }
       }
       }
}


public class RunSync {

   public static void main(String[] args) {
      Temp temp1 = new Temp();
      Temp temp2 = new Temp();
     
      new CustomThread1(temp1, temp2, "one");
      new CustomThread2(temp1, temp2, "two");
    }
}
  
As soon as you will run this program, it will print following output and will go to dead lock.


You will have to explicitly stop the program. This is because in this program the thread 1 has synchronized object 1 and thread 2 has synchronized the object 2. Hence both are waiting for each other. Always avoid such type of programming.

interrupt() method:
this is a Thread class method which is used to request to the JVM to terminate a thread. As we cannot force JVM for garbage collection, we can only request by using System.gc() method, similarly we cannot force JVM to terminate a thread. We can only request. In each class there is boolean flag which is set to true if interrupt request has been raised. In 99% cases JVM does not terminate the thread,









<< Previous Chapter Next Chapter >>



No comments:

Post a Comment