YouTip LogoYouTip

Thread Deadlock

Java Example Java Example

\n\n

Deadlock is a situation where multiple threads are blocked, with one or all of them waiting for a resource to be released. Since the threads are blocked indefinitely, the program cannot terminate normally.

\n\n

The four necessary conditions for a Java deadlock are:

\n\n
    \n
  1. Mutual Exclusion: When a resource is used (held) by one thread, other threads cannot use it.
  2. \n
  3. No Preemption: A resource requester cannot forcibly take the resource from the holder; the resource can only be released voluntarily by the holder.
  4. \n
  5. Hold and Wait: A resource requester holds onto existing resources while requesting additional ones.
  6. \n
  7. Circular Wait: There exists a waiting cycle: P1 holds P2's resource, P2 holds P3's resource, and P3 holds P1's resource, forming a circular chain.
  8. \n
\n\n

When all four conditions are met, a deadlock occurs. Of course, breaking any one of these conditions can resolve the deadlock. Below is a Java code simulation of deadlock occurrence.

\n\n

Methods to solve deadlock include using synchronized or explicit locks with Lock.

\n\n

If locks are used improperly and multiple objects need to be locked simultaneously, a deadlock can occur, as shown below:

\n\n

LockTest.java File

\n\n
import java.util.Date;\n\npublic class LockTest {\n    public static String obj1 = "obj1";\n    public static String obj2 = "obj2";\n\n    public static void main(String[] args) {\n        LockA la = new LockA();\n        new Thread(la).start();\n        LockB lb = new LockB();\n        new Thread(lb).start();\n    }\n}\n\nclass LockA implements Runnable {\n    public void run() {\n        try {\n            System.out.println(new Date().toString() + " LockA Start execution");\n            while (true) {\n                synchronized (LockTest.obj1) {\n                    System.out.println(new Date().toString() + " LockA Locked obj1");\n                    Thread.sleep(3000);\n                    synchronized (LockTest.obj2) {\n                        System.out.println(new Date().toString() + " LockA Locked obj2");\n                        Thread.sleep(60 * 1000);\n                    }\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n}\n\nclass LockB implements Runnable {\n    public void run() {\n        try {\n            System.out.println(new Date().toString() + " LockB Start execution");\n            while (true) {\n                synchronized (LockTest.obj2) {\n                    System.out.println(new Date().toString() + " LockB Locked obj2");\n                    Thread.sleep(3000);\n                    synchronized (LockTest.obj1) {\n                        System.out.println(new Date().toString() + " LockB Locked obj1");\n                        Thread.sleep(60 * 1000);\n                    }\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n}
\n\n

The output of the above code is:

\n\n
Tue May 05 10:51:06 CST 2015 LockB Start execution\nTue May 05 10:51:06 CST 2015 LockA Start execution\nTue May 05 10:51:06 CST 2015 LockB Locked obj2\nTue May 05 10:51:06 CST 2015 LockA Locked obj1
\n\n

At this point, a deadlock occurs.

\n\n

To solve this problem, instead of using explicit locks, we can use semaphores for control.

\n\n

A semaphore can control how many threads can access a resource. Here, we specify that only one thread can access it, achieving a similar effect to locking. Additionally, a semaphore can specify a timeout for acquisition, which we can use for extra handling.

\n\n

For cases where acquisition fails, common approaches include retrying, specifying a number of attempts, or exiting immediately.

\n\n

Consider the following code:

\n\n

UnLockTest.java File

\n\n
import java.util.Date;\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\n\npublic class UnLockTest {\n    public static String obj1 = "obj1";\n    public static final Semaphore a1 = new Semaphore(1);\n    public static String obj2 = "obj2";\n    public static final Semaphore a2 = new Semaphore(1);\n\n    public static void main(String[] args) {\n        LockAa la = new LockAa();\n        new Thread(la).start();\n        LockBb lb = new LockBb();\n        new Thread(lb).start();\n    }\n}\n\nclass LockAa implements Runnable {\n    public void run() {\n        try {\n            System.out.println(new Date().toString() + " LockA Start execution");\n            while (true) {\n                if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) {\n                    System.out.println(new Date().toString() + " LockA Locked obj1");\n                    if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) {\n                        System.out.println(new Date().toString() + " LockA Locked obj2");\n                        Thread.sleep(60 * 1000);\n                    } else {\n                        System.out.println(new Date().toString() + "LockA Lock obj2 failed");\n                    }\n                } else {\n                    System.out.println(new Date().toString() + "LockA Lock obj1 failed");\n                }\n                UnLockTest.a1.release();\n                UnLockTest.a2.release();\n                Thread.sleep(1000);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n}\n\nclass LockBb implements Runnable {\n    public void run() {\n        try {\n            System.out.println(new Date().toString() + " LockB Start execution");\n            while (true) {\n                if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) {\n                    System.out.println(new Date().toString() + " LockB Locked obj2");\n                    if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) {\n                        System.out.println(new Date().toString() + " LockB Locked obj1");\n                        Thread.sleep(60 * 1000);\n                    } else {\n                        System.out.println(new Date().toString() + "LockB Lock obj1 failed");\n                    }\n                } else {\n                    System.out.println(new Date().toString() + "LockB Lock obj2 failed");\n                }\n                UnLockTest.a1.release();\n                UnLockTest.a2.release();\n                Thread.sleep(10 * 1000);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n}
\n\n

The output of the above example code is:

\n\n
Tue May 05 10:59:13 CST 2015 LockA Start execution\nTue May 05 10:59:13 CST 2015 LockB Start execution\nTue May 05 10:59:13 CST 2015 LockB Locked obj2\nTue May 05 10:59:13 CST 2015 LockA Locked obj1\nTue May 05 10:59:14 CST 2015LockB Lock obj1 failed\nTue May 05 10:59:14 CST 2015LockA Lock obj2 failed\nTue May 05 10:59:15 CST 2015 LockA Locked obj1\nTue May 05 10:59:15 CST 2015 LockA Locked obj2
\n\n

Java Example Java Example

← Thread MonitorCpp Friend Functions β†’