Thread Synchronization in Java:
What is Synchronization?
When two or more threads access the same field at the same time, it results in concurrency issues and the results will be wrong. Synchronization makes sure that, even if we start multiple threads only one thread can access a resource at a time. This is known as Synchronization.

Points to Note:

  • When we use synchronize, it means only one thread can access a resource at a time. Means that the thread acquires a lock, so no other threads can access it.
  • Only Methods can be synchronized, not classes
  • A class can have both synchronized and non-synchronized methods
  • When multiple threads are trying to access synchronized method with same instance object, Only one thread can acquire the lock. Another thread has to wait till one thread finishes the operation and releases the lock.
  • When thread goes to sleep, it does not release the lock.
  • When a thread does not get lock, it has to wait till the executing thread releases the lock. But, that does not mean the longest waiting thread for that lock will get it, the lock may be acquired by any thread that will waiting for the lock irrespective of thread waiting for long or short time.

DeadLock:
One of the most famous issue of Threads. Deadlock happens when, Consider there are two threads Thread A and Thread B.  Thread A enters a synchronization code 1 and acquires lock. Thread B enters another synchronized code 2 and acquires a lock.  A needs access to Synchronized code 2, to complete its process. B Needs access to Synchronized code 1 to complete. This situation where A waits for B to release the lock to complete and B inturn waits for A to release the lock to complete. Both threads wait for each other to release the lock. This is known a Deadlock.

In short, Thread 1 waits for Thread 2 to release the lock and Thread 2 waits for Thread 1 to release the lock.

wait(), notify() and notifyall():
Important Note:
These three methods are must be called within synchronized code. These methods cannot be called on an object unless object has the lock.

 

Class Objects Class Threads
wait(), notify(), notifyall() sleep(), join(), yield()

wait():
It informs the thread to release the lock and go to sleep, until some other thread enters and calls the notify() method.

notify():
It wakes up one thread from sleep that called wait() method from same object. But this does not mean that wake-up thread acquires lock. This method just notifies the sleeping thread that it can wake up. The waked up thread needs to wait till the thread (that called notify()) completes its process.

notifyall():
This method wakes up all the threads that called wait() in the same object.

Example:
Synchronized (object){
object.wait();
object.notify();
object.notifyall();
}

We are calling all these methods with object in synchronized context.

Now let us see an example with and without synchronization,
Example 1: Without Synchronization
We have an variable amount(100) in class balance. There are two persons trying to withdraw that money. They will make 2 transactions (50) each. Now let us see the output without synchronization.

package Threads;

/**
 *
 * @author Vikram
 */
class balance{
    int amount = 100;

    public int getAmount() {
        return amount;
    }

    public void setAmount(int amount) {
        this.amount = amount;
    }
    
    void amountDetected(int n){
        amount = amount - n;
    }
}
public class ThreadSynchronization implements Runnable {
    static ThreadSynchronization threadSync = new ThreadSynchronization();
    balance b = new balance();
    
   private void withdrawal(int amount){
        System.out.println("Amount Available: "+b.getAmount());
        for(int i=100; i>=0;i=i-20){
            
            if(b.getAmount() > 0){
            
            System.out.println("Amount Withdrawed by "+Thread.currentThread().getName()+" Amount is 50");
            b.amountDetected(amount);
            System.out.println("Amount Available: "+b.getAmount());
            try{
                Thread.sleep(500);
            }
            catch(Exception e){
                e.printStackTrace();
            }
        }
        else
        {
            System.out.println("No Amount Left for: "+Thread.currentThread().getName());
            break;
        }
        }
       
    }
    
    @Override
    public void run() {
        threadSync.withdrawal(50);
       
    }
    
    public static void main(String args[]){
    ThreadSynchronization threadSync1 = new ThreadSynchronization();
    Thread thread1 = new Thread(threadSync1);
    Thread thread2 = new Thread(threadSync1);
    thread1.setName("Person 1");
    thread2.setName("Person 2");
    thread1.start();
    thread2.start();
    
    }
}

Output: Without Synchronization
op1
From output, We can see the concurrency issues. Amount withdrawn by person 2 is 50, But still we can see amount available is 50 and 100. This issue will happen when multiple threads try to access same resource.

Example 2: With Synchronization:
Now with the same example, We are going to add synchronize to the method. So, Only one thread will get the lock and process and another thread will wait till the executing thread releases the lock.
Expected Output:
One thread should access the block at a time, it should make 2 transactions. The balance amount will be 0. So no amount should be available for another thread.

package Threads;

/**
 *
 * @author Admin
 */
class balance{
    int amount = 100;

    public int getAmount() {
        return amount;
    }

    public void setAmount(int amount) {
        this.amount = amount;
    }
    
    void amountDetected(int n){
        amount = amount - n;
    }
}
public class ThreadSynchronization implements Runnable {
    static ThreadSynchronization threadSync = new ThreadSynchronization();
    balance b = new balance();
    
  <strong>synchronized private void withdrawal(int amount){</strong>
        System.out.println("Amount Available: "+b.getAmount());
        for(int i=100; i>=0;i=i-20){
            
            if(b.getAmount() > 0){
            
            System.out.println("Amount Withdrawed by "+Thread.currentThread().getName()+" Amount is 50");
            b.amountDetected(amount);
            System.out.println("Amount Available: "+b.getAmount());
            try{
                Thread.sleep(500);
            }
            catch(Exception e){
                e.printStackTrace();
            }
        }
        else
        {
            System.out.println("No Amount Left for: "+Thread.currentThread().getName());
            break;
        }
        }
       
    }
    
    @Override
    public void run() {
        threadSync.withdrawal(50);
       
    }
    
    public static void main(String args[]){
    ThreadSynchronization threadSync1 = new ThreadSynchronization();
    Thread thread1 = new Thread(threadSync1);
    Thread thread2 = new Thread(threadSync1);
    thread1.setName("Person 1");
    thread2.setName("Person 2");
    thread1.start();
    thread2.start();
    
    }
}

Output: With Synchronized:
op2
Now we can see, Person 1 makes two successful transactions and amount becomes 0. When person 2 tries to withdraw money, there is no money. Only one thread access the resource at a time and another thread waits till execution thread completes its process.

 

By Sri

Leave a Reply

Your email address will not be published. Required fields are marked *