🎉 Special Offer !    Code: GET300OFF    Flat ₹300 OFF on every Java Course
Grab Deal 🚀

Deadlock in Multithreading  


Introduction
  • Deadlock is the situation where two or more threads are waiting for each other forever, and none of them can continue.
  • Example in real life:
    • Person A is holding Pen and waiting for Paper.
    • Person B is holding Paper and waiting for Pen.
    • Both keep waiting forever → Deadlock.
  • Program:
    class Resource
    {
        
    }
    class MyThread1 extends Thread
    {
        Resource res1, res2;
        MyThread1(Resource res1, Resource res2)
        {
            this.res1=res1;
            this.res2=res2;
        }
        public void run()
        {
            synchronized(res1)
            {
                System.out.println("Thread 1 has allocated resource 1");
                try
                {
                    Thread.sleep(1000);
                }
                catch(Exception e)
                {
                    System.out.println(e);
                }
                synchronized(res2)
                {
                    System.out.println("Thread 1 has allocated resource 2");
                }
            }
        }
    }
    class MyThread2 extends Thread
    {
        Resource res1, res2;
        MyThread2(Resource res1, Resource res2)
        {
            this.res1=res1;
            this.res2=res2;
        }
        public void run()
        {
            synchronized(res2)
            {
                System.out.println("Thread 2 has allocated resource 2");
                try
                {
                    Thread.sleep(1000);
                }
                catch(Exception e)
                {
                    System.out.println(e);
                }
                synchronized(res1)
                {
                    System.out.println("Thread 2 has allocated resource 1");
                }
            }
        }
    }
    public class DeadlockDemo
    {
        public static void main(String[] args)
        {
            Resource res1=new Resource();
            Resource res2=new Resource();
            
            MyThread1 t1=new MyThread1(res1, res2);
            t1.start();
            
            MyThread2 t2=new MyThread2(res1, res2);
            t2.start();
        }
    }

Preventing Deadlocks:
  • Deadlocks can be tricky to find and fix, but we can reduce the chances of them happening by following some good practices.
  • We cannot completely remove the possibility of deadlocks, but we can make them very rare.
  • Here are some simple tips to prevent deadlocks:
    1. Avoid Nested Locks
      • Deadlocks mostly happen when a thread locks one resource and then tries to lock another resource.
      • Rule: If you already have a lock on one resource, avoid locking another resource inside it.
      • Example:
        synchronized(res1)
        {
            synchronized(res2)    // Avoid such nested locking if possible
            {
                // critical section
            }
        }
    2. Avoid Unnecessary Locks
      • Only lock what is really needed.
      • If locking is not required, don’t use synchronized.
      • The fewer locks you use, the fewer chances of deadlock.
    3. Use Thread Join with Timeout
      • Sometimes deadlock happens because one thread waits forever for another thread to finish.
      • Solution: Use join() with a timeout so the waiting thread doesn’t wait forever.
      • Example:
        thread.join(1000); // wait for max 1 second
    4. Lock Resources in a Fixed Order
      • If you really must take multiple locks (nested locks), always lock them in the same order in every thread.
      • This ensures two threads will not wait for each other forever.
      • Example:
        // Thread 1
        synchronized(res1)
        {
            synchronized(res2)
            {
                // work with res1 and res2
            }
        }
        
        // Thread 2 - use same order
        synchronized(res1)
        {
            synchronized(res2)
            {
                // work with res1 and res2
            }
        }
      • Rule: Decide an order (e.g., res1 then res2) and follow the same order everywhere.
  • Key Idea: Keep locks simple and consistent. Avoid taking multiple locks at the same time and use timeouts where possible.
  • Program (Deadlock Problem Resolved):
    class Resource
    {
    
    }
    class MyThread1 extends Thread
    {
        Resource res1, res2;
        MyThread1(Resource res1, Resource res2)
        {
            this.res1=res1;
            this.res2=res2;
        }
        public void run()
        {
            synchronized(res1)
            {
                System.out.println("Thread 1 has allocated resource 1");
                try
                {
                    Thread.sleep(1000);
                }
                catch(Exception e)
                {
                    System.out.println(e);
                }
                synchronized(res2)
                {
                    System.out.println("Thread 1 has allocated resource 2");
                }
            }
        }
    }
    class MyThread2 extends Thread
    {
        Resource res1, res2;
        MyThread2(Resource res1, Resource res2)
        {
            this.res1=res1;
            this.res2=res2;
        }
        public void run()
        {
            // FIX: Lock res1 first, then res2 (same as MyThread1)
            synchronized(res1)
            {
                System.out.println("Thread 2 has allocated resource 1");
                try
                {
                    Thread.sleep(1000);
                }
                catch(Exception e)
                {
                    System.out.println(e);
                }
                synchronized(res2)
                {
                    System.out.println("Thread 2 has allocated resource 2");
                }
            }
        }
    }
    public class DeadlockDemo
    {
        public static void main(String[] args)
        {
            Resource res1=new Resource();
            Resource res2=new Resource();
    
            MyThread1 t1=new MyThread1(res1, res2);
            t1.start();
    
            MyThread2 t2=new MyThread2(res1, res2);
            t2.start();
        }
    }