Java-Synchronization

Synchronization

So far, we have seen threads that use their own data and methods provided inside their run( ) methods. What happens when they try to use data and methods outside themselves? On such occasions, they may compete for the same resources and may lead to serious problems. For example, one thread may try to read a record from a file while another is still writing to the same file. Depending on the situation, we may get strange results. Java enables us to overcome this problem using a technique known as synchronization.

The access to a method shall be synchronized by specifying the synchronized keyword as a modifier in the method declaration. When a thread starts executing a synchronized instance method, it automatically gets a logical lock on the object that contains the synchronized method. This lock will remain as long as that thread is executing that synchronized method. When the lock is present, no other thread will be allowed entry into that object. The lock will be automatically released, when that thread completes the execution of that synchronized method. Any other executable thread will be allowed entry into that locked object only when it does not have a lock. This is how the JVM carefully coordinates the access to a common resource by multiple threads.

To understand the need for synchronization, let’s begin with a simple example that does not use it—but should. The following program has three simple classes. The first one, DataBase, has a single method named showData( ). This method tries to print the data array values. The interesting thing to notice is that before showData( ) prints the array value, it calls Thread.sleep(1000), which pauses the current thread for one second.

The constructor of the next class, DataBaseAccessor, takes a reference to an instance of the DataBase class, which are stored in tb. The constructor also starts a thread that will call run( ) method. The thread is started immediately. The run( ) method of DataBaseAccessor calls the showData( ) method on the tb instance of DataBase, passing in the tname string. Finally, the Javaapp class starts by creating a single instance of DataBase, and two instances of DataBaseAccessor. The same instance of DataBase is passed to each DataBaseAccessor.

Program : without Synchronization

sdsds

This results in the mixed-up output of the array values. In this program, nothing exists to stop two threads from calling the same method, on the same object, at the same time. This is known as a race condition, because the two threads are racing each other to complete the method. This example used sleep( ) to make the effects repeatable and obvious. In most situations, a race condition is more subtle and less predictable, because you can’t be sure when the context switch will occur. This can cause a program to run right one time and wrong the next.

To fix the previous program, you must serialize access to showData( ). That is, you must restrict its access to only one thread at a time. To do this, you simply need to precede showData( )’s definition with the keyword synchronized. The following program is an improved version of the previous program that uses synchronization to prevents other threads from entering showData( ) while another thread is using it.

Program : with Synchronized

final0

Program Source : without Synchronized

class DataBase {
   
    int[] data = {10,20,30,40,50,60,70,80,90,100};
    int dataPosition = 0;
    
    void showData(String tname)
    {
        for(int i=0;i<5;i++)
        {
            try{
                Thread.sleep(1000);
                System.out.println(tname+" : data["+dataPosition+"] = "+data[dataPosition]);
                dataPosition++;
            }catch(InterruptedException ie)
            {
                System.out.println("Thread Interrupted");
            }
        }
    }
}

class DataBaseAccessor extends Thread{

    DataBase tb;
    String tname;

    DataBaseAccessor(DataBase tb,String tname)
    {
        this.tb = tb;
        this.tname = tname;
        start();
    }

    public void run()
    {
        tb.showData(tname);
        System.out.println("Exiting "+tname);
    }
}

public class Javaapp {

    public static void main(String[] args) {
    
        DataBase tb = new DataBase();
        DataBaseAccessor th1 = new DataBaseAccessor(tb,"Thread1");
        DataBaseAccessor th2 = new DataBaseAccessor(tb,"Thread2");
    }
}

Program Source : with Synchronized

class DataBase {
   
    int[] data = {10,20,30,40,50,60,70,80,90,100};
    int dataPosition = 0;
    
    synchronized void showData(String tname)
    {
        for(int i=0;i<5;i++)
        {
            try{
                Thread.sleep(1000);
                System.out.println(tname+" : data["+dataPosition+"] = "+data[dataPosition]);
                dataPosition++;
            }catch(InterruptedException ie)
            {
                System.out.println("Thread Interrupted");
            }
        }
    }
}

class DataBaseAccessor extends Thread{

    DataBase tb;
    String tname;

    DataBaseAccessor(DataBase tb,String tname)
    {
        this.tb = tb;
        this.tname = tname;
        start();
    }

    public void run()
    {
        tb.showData(tname);
        System.out.println("Exiting "+tname);
    }
}

public class Javaapp {

    public static void main(String[] args) {
    
        DataBase tb = new DataBase();
        DataBaseAccessor th1 = new DataBaseAccessor(tb,"Thread1");
        DataBaseAccessor th2 = new DataBaseAccessor(tb,"Thread2");
    }
}

Leave a Comment