Java Thread Deadlock Example and Thread Dump Analysis using VisualVM

Deadlock is a condition where several threads are blocking forever, waiting for the other to finish but they never do. This situation results in the application hanging when it reaches this segment of code. It may appear sporadic. These situations may, at times be hard to pinpoint and track down. This tutorial will discuss situations that will lead to Java Thread deadlock conditions and how they can be avoided. In addition, we will discuss using Java VisualVM, which comes bundled with every JDK version and will aid the development team to pinpoint and analyze the source of the deadlock conditions.

Deadlock Scenario

Consider the following scenario (image below) where Thread 1 is holding a lock on Resource A, does some work and then requests Resource B in order to perform some additional work while not releasing the lock on Resource A. Now, at the same time, Thread 2 is holding a lock on Resource B, it does some work and then requests Resource A in order for it to perform some additional work while not releasing the lock on Resource B. Take a minute to ponder this question. What do you think will happen in this scenario?

Any guesses? If you guessed – deadlock, you’re absolutely right. This scenario happens because Thread 1 has put a lock on a resource that thread 2 wants and thread 2 has a lock on a resource that thread 1 wants while it holds a lock on a resource thread 2 wants… Each thread will in essence, wait for the other to finish the work and release the lock but they never get to finish as they are both waiting for each other — forever!

thread_deadlock_scenario

DeadLockExample.java

package com.avaldes.tutorials;

public class DeadLockExample {
  public static Object ResourceA = new Object(); 
  public static Object ResourceB = new Object();
  
  public static void main(String[] args) {
    Thread t1 = new Thread(new Worker1(), "Thread_1");
    Thread t2 = new Thread(new Worker2(), "Thread_2");
    
    t1.start();
    t2.start();
  }
  
  private static class Worker1 implements Runnable {
    public void run() {
      synchronized (ResourceA) {
        System.out.println("Worker1: Holding ResourceA...");
        doSomeWork();
        System.out.println("Worker1: Waiting for ResourceB...");
        synchronized (ResourceB) {
           System.out.println("Worker1: Holding ResourceA & ResourceB...");
           doSomeOtherWork();
        }
      }
    }
    
    public void doSomeWork() {
      try {
        System.out.println("Worker1: Doing Some Work...");
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    
    public void doSomeOtherWork() {
      try {
        System.out.println("Worker1: Doing Some Other Work...");
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
  
  private static class Worker2 implements Runnable {
    public void run() {
      synchronized (ResourceB) {
        System.out.println("Worker2: Holding ResourceB...");
        doSomeWork();
        System.out.println("Worker2: Waiting for ResourceA...");
        synchronized (ResourceA) {
           System.out.println("Worker2: Holding ResourceB & ResourceA...");
           doSomeOtherWork();
        }
      }
    }
    
    public void doSomeWork() {
      try {
        System.out.println("Worker2: Doing Some Work...");
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    
    public void doSomeOtherWork() {
      try {
        System.out.println("Worker2: Doing Some Other Work...");
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }

  }
  
}

Output

Worker1: Holding ResourceA...
Worker1: Doing Some Work...
Worker2: Holding ResourceB...
Worker2: Doing Some Work...
Worker1: Waiting for ResourceB...
Worker2: Waiting for ResourceA...

Use Java VisualVM to analyze Thread Blocking and Deadlocks

Whenever you run multithreaded applications it is a good idea to launch VisualVM and analyze how your multithreaded application is working. You can monitor things like CPU usage, memory usage, number of classes loaded and thread details. In addition, you will be able to sample your code and see where your application is spending most of its time for performance tuning. However, for the purpose of this tutorial we will be using it to monitor threads and perform a thread dump once blocking has occurred.

visualVM-for-Java-thread-blocking-and-thread-dumps

As soon as the application is launched and I activate the “Threads” tab I can see that blocking is occurring and we, in fact, have a deadlock condition. Please ensure the threads visualization checkbox is checked otherwise the tab will be mostly blank.

deadlock_screenshot_visualVM

As you can see from the screenshot, some of the threads are fine. Green means thread is running. Yellow means it is waiting from some resource to become available, or waiting for notify/notifyAll from another thread. Finally, red means that the thread is waiting to gain access over a monitor (in other words, it has reached a synchronized block or is waiting for some kind of lock on a resource or object). As you can see from the screenshot, both Thread_1 and Thread_2 are waiting and in this case, blocking each other causing a deadlock condition.

Note

If you feel your application code has deadlocks, you can Java VisualVM application the came with your version of the Java JDK. In my example, I am using VisualVM that came with my JDK version 1.6.0_45. If you open up the Threads tab you will be able to see all threads that are currently running in your process. Using this application will help you analyze running JVM applications and alert you when blocking has been detected. At that point, you can click on Thread Dump Button on the far upper right corner for detailed thread dump (see below).

Reviewing the Thread Dump from Java VisualVM

2013-03-26 12:26:22
Full thread dump Java HotSpot(TM) 64-Bit Server VM (20.45-b01 mixed mode):

"RMI TCP Connection(5)-192.168.1.223" daemon prio=6 tid=0x0000000007e3a000 nid=0x27dc runnable [0x0000000009bef000]
   java.lang.Thread.State: RUNNABLE
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.read(SocketInputStream.java:129)
	at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
	at java.io.BufferedInputStream.read(BufferedInputStream.java:237)
	- locked <0x00000007d63f9930> (a java.io.BufferedInputStream)
	at java.io.FilterInputStream.read(FilterInputStream.java:66)
	at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:517)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
	at java.lang.Thread.run(Thread.java:662)

   Locked ownable synchronizers:
	- <0x00000007d62b1328> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

"JMX server connection timeout 19" daemon prio=6 tid=0x00000000087d8800 nid=0x10cc in Object.wait() [0x000000000801f000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000007d626c2f0> (a [I)
	at com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout.run(ServerCommunicatorAdmin.java:150)
	- locked <0x00000007d626c2f0> (a [I)
	at java.lang.Thread.run(Thread.java:662)

   Locked ownable synchronizers:
	- None

"RMI TCP Connection(4)-192.168.1.223" daemon prio=6 tid=0x0000000007c3f800 nid=0x27a0 runnable [0x0000000008b1f000]
   java.lang.Thread.State: RUNNABLE
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.read(SocketInputStream.java:129)
	at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
	at java.io.BufferedInputStream.read(BufferedInputStream.java:237)
	- locked <0x00000007d6262e20> (a java.io.BufferedInputStream)
	at java.io.FilterInputStream.read(FilterInputStream.java:66)
	at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:517)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
	at java.lang.Thread.run(Thread.java:662)

   Locked ownable synchronizers:
	- <0x00000007800286b8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

"JMX server connection timeout 16" daemon prio=6 tid=0x0000000007b2e800 nid=0x2794 in Object.wait() [0x000000000821f000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x000000078002c070> (a [I)
	at com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout.run(ServerCommunicatorAdmin.java:150)
	- locked <0x000000078002c070> (a [I)
	at java.lang.Thread.run(Thread.java:662)

   Locked ownable synchronizers:
	- None

"RMI Scheduler(0)" daemon prio=6 tid=0x0000000007b25800 nid=0x1c8c waiting on condition [0x000000000811f000]
   java.lang.Thread.State: TIMED_WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x000000078002a0a0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:196)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2025)
	at java.util.concurrent.DelayQueue.take(DelayQueue.java:164)
	at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:609)
	at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:602)
	at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:957)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:917)
	at java.lang.Thread.run(Thread.java:662)

   Locked ownable synchronizers:
	- None

"RMI TCP Accept-0" daemon prio=6 tid=0x00000000068a0000 nid=0x15c4 runnable [0x0000000007a9f000]
   java.lang.Thread.State: RUNNABLE
	at java.net.PlainSocketImpl.socketAccept(Native Method)
	at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:408)
	- locked <0x000000078002c140> (a java.net.SocksSocketImpl)
	at java.net.ServerSocket.implAccept(ServerSocket.java:462)
	at java.net.ServerSocket.accept(ServerSocket.java:430)
	at sun.management.jmxremote.LocalRMIServerSocketFactory$1.accept(LocalRMIServerSocketFactory.java:34)
	at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(TCPTransport.java:369)
	at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(TCPTransport.java:341)
	at java.lang.Thread.run(Thread.java:662)

   Locked ownable synchronizers:
	- None

"DestroyJavaVM" prio=6 tid=0x000000000002b800 nid=0x1668 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
	- None

"Thread_2" prio=6 tid=0x00000000067a2000 nid=0x20e8 waiting for monitor entry [0x00000000071ff000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.avaldes.tutorials.DeadLockExample$Worker2.run(DeadLockExample.java:44)
	- waiting to lock <0x000000078002a278> (a java.lang.Object)
	- locked <0x000000078002a288> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:662)

   Locked ownable synchronizers:
	- None

"Thread_1" prio=6 tid=0x000000000679f000 nid=0x155c waiting for monitor entry [0x00000000070ff000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.avaldes.tutorials.DeadLockExample$Worker1.run(DeadLockExample.java:22)
	- waiting to lock <0x000000078002a288> (a java.lang.Object)
	- locked <0x000000078002a278> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:662)

   Locked ownable synchronizers:
	- None

"Low Memory Detector" daemon prio=6 tid=0x000000000678c800 nid=0x1f14 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
	- None

"C2 CompilerThread1" daemon prio=10 tid=0x0000000006770800 nid=0x1d14 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
	- None

"C2 CompilerThread0" daemon prio=10 tid=0x000000000675e000 nid=0x1984 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
	- None

"Attach Listener" daemon prio=10 tid=0x000000000675a000 nid=0x17d8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
	- None

"Signal Dispatcher" daemon prio=10 tid=0x0000000006755000 nid=0x22b4 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
	- None

"Finalizer" daemon prio=8 tid=0x0000000006705800 nid=0x25e8 in Object.wait() [0x00000000069ff000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x000000078002a550> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118)
	- locked <0x000000078002a550> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134)
	at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:171)

   Locked ownable synchronizers:
	- None

"Reference Handler" daemon prio=10 tid=0x00000000003ea800 nid=0x1f0c in Object.wait() [0x00000000066ff000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x0000000780028100> (a java.lang.ref.Reference$Lock)
	at java.lang.Object.wait(Object.java:485)
	at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)
	- locked <0x0000000780028100> (a java.lang.ref.Reference$Lock)

   Locked ownable synchronizers:
	- None

"VM Thread" prio=10 tid=0x00000000003e2800 nid=0x1234 runnable 

"GC task thread#0 (ParallelGC)" prio=6 tid=0x0000000000337000 nid=0x19a0 runnable 

"GC task thread#1 (ParallelGC)" prio=6 tid=0x0000000000339000 nid=0x2590 runnable 

"GC task thread#2 (ParallelGC)" prio=6 tid=0x000000000033a800 nid=0x1c80 runnable 

"GC task thread#3 (ParallelGC)" prio=6 tid=0x000000000033c800 nid=0x18fc runnable 

"VM Periodic Task Thread" prio=10 tid=0x000000000679e000 nid=0x20f4 waiting on condition 

JNI global references: 1041


Found one Java-level deadlock:
=============================
"Thread_2":
  waiting to lock monitor 0x0000000006702bd8 (object 0x000000078002a278, a java.lang.Object),
  which is held by "Thread_1"
"Thread_1":
  waiting to lock monitor 0x00000000067053e0 (object 0x000000078002a288, a java.lang.Object),
  which is held by "Thread_2"

Java stack information for the threads listed above:
===================================================
"Thread_2":
	at com.avaldes.tutorials.DeadLockExample$Worker2.run(DeadLockExample.java:44)
	- waiting to lock <0x000000078002a278> (a java.lang.Object)
	- locked <0x000000078002a288> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:662)
"Thread_1":
	at com.avaldes.tutorials.DeadLockExample$Worker1.run(DeadLockExample.java:22)
	- waiting to lock <0x000000078002a288> (a java.lang.Object)
	- locked <0x000000078002a278> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:662)

Found 1 deadlock.

Steps to Avoid Deadlocks in Multithreaded Java Applications

There are several steps you can take to avoid or minimize deadlock situations.

  • Lock Order – As you saw from my previous example, deadlocks occur when multiple threads accessed resources and obtained locks in different order. Ensuring the same lock ordering sequence will help avoid this common pitfall.
  • Corrected Snippet for proper Lock Order

    synchronized (ResourceA) {
      System.out.println("Worker1: Holding ResourceA...");
      doSomeWork();
      System.out.println("Worker1: Waiting for ResourceB...");
      synchronized (ResourceB) {
        System.out.println("Worker1: Holding ResourceA & ResourceB...");
        doSomeOtherWork();
      }
    }
    ...
    synchronized (ResourceA) {
      System.out.println("Worker2: Holding ResourceA...");
      doSomeWork();
      System.out.println("Worker2: Waiting for ResourceB...");
      synchronized (ResourceB) {
        System.out.println("Worker2: Holding ResourceA & ResourceB...");
        doSomeOtherWork();
      }
    }
    
  • Nested Locks – A nested lock occurs when you obtain a lock on one resource and then attempt obtaining one or more additional locks without releasing current locks. This scenario also existed in my example as I was holding on to a lock on Resource A while trying to acquire a lock on Resource B without releasing Resource A.
  • Corrected Snippet from Nested to Sequential Locks

    synchronized (ResourceA) {
    	System.out.println("Worker1: Holding ResourceA...");
    	doSomeWork();
    }
    System.out.println("Worker1: Waiting for ResourceB...");
    synchronized (ResourceB) {
    	System.out.println("Worker1: Holding ResourceB...");
    	doSomeOtherWork();
    }
    ...
    
    synchronized (ResourceA) {
    	System.out.println("Worker2: Holding ResourceA...");
    	doSomeWork();
    }
    System.out.println("Worker2: Waiting for ResourceB...");
    synchronized (ResourceB) {
    	System.out.println("Worker2: Holding ResourceB...");
    	doSomeOtherWork();
    }
    
  • Avoid Intrinsic Locks – When locking multiple resources avoid using intrinsic locks, instead use explicit locks via timed tryLock. Using intrinsic locks will wait forever if the lock cannot be acquired. By using a timeout, you retain control when it fails for some unexpected reason. You are then at liberty to wait for some specified duration and try again at a later time.
  • Narrow Lock Scope – When locking resources always ensure you hold the lock on the critical resource for as brief a time as possible. Move all unnecessary code outside of the synchronized block of code.

Related Posts

  • Java Thread, Concurrency and Multithreading Tutorial
    This Java Thread tutorial will give you a basic overview on Java Threads and introduce the entire tutorial series on concurrency and multithreading. From here, you will learn about many java thread concepts like: Thread States, Thread Priority, Thread Join, and ThreadGroups. In addition, you will learn about using the volatile keyword and examples on using wait, notify and notifyAll.
  • Java Thread States - Life Cycle of Java Threads
    Get a basic understanding of the various thread states. Using the state transition diagram we show the various states for a Java thread and the events that cause the thread to jump from one state to another.
  • Creating Java Threads Example
    In this post we cover creating Java Threads using the two mechanisms provided in Java, that is, by extending the Thread class and by implementing Runnable interface for concurrent programming.
  • Java Thread Priority Example
    In this post we cover Thread priorities in Java. By default, a java thread inherits the priority (implicit) of its parent thread. Using the setPriority() method you can increase or decrease the thread priority of any java thread.
  • Java ThreadGroup Example
    Sometimes we will need to organize and group our threads into logical groupings to aid in thread management. By placing threads in a threadGroup all threads in that group can be assigned properties as a set, instead of going through the tedious task of assigning properties individually.
  • Java Thread Sleep Example
    We seem to use this method very often to temporarily suspend the current threads execution for a specific period of time. Let's spend some time and familiarize ourselves with what this method actually does.
  • Java Thread Join Example
    In Java, using Thread.join() causes the current thread to wait until the specified thread dies. Using this method allows us to impose an order such that we can make one thread wait until the other completes doing what it needed to do, such as completing a calculation.
  • Examining Volatile Keyword with Java Threads
    When we declare a field as volatile, the JVM will guarantee visibility, atomicity and ordering of the variable. Without it the data may be cached locally in CPU cache and as a result changes to the variable by another thread may not be seen by all other threads resulting in inconsistent behaviour.
  • Java Threads Wait, Notify and NotifyAll Example
    The purpose of using notify() and notifyAll() is to enable threads to communicate with one another via some object on which to performing the locking. A thread using the wait() method must own a lock on the object. Once wait() is called, the thread releases the lock, and waits for another thread to either call notify() or notifyAll() method.
  • Java Thread Deadlock Example and Thread Dump Analysis using VisualVM
    Deadlock is a condition where several threads are blocking forever, waiting for the other to finish but they never do. This tutorial will discuss situations that will lead to Java Thread deadlock conditions and how they can be avoided. In addition, we will discuss using Java VisualVM to pinpoint and analyze the source of the deadlock conditions.
  • Java Thread Starvation and Livelock with Examples
    Starvation occurs when a thread is continually denied access to resources and as a result it is unable to make progress. Thread liveLock is a condition that closely resembles deadlock in that several processes are blocking each other. But with livelock, a thread is unable to make any progress because every time it tries the operation always fails.
  • Java Synchronization and Thread Safety Tutorial with Examples
    One of Java's many strengths come from the fact that it supports multithreading by default as has so from the very onset. One of the mechanisms that Java uses for this is via synchronization. When we use the synchronized keyword in Java we are trying limit the number of threads that can simultaneously access and modify a shared resource. The mechanism that is used in Java's synchronization is called a monitor.
  • Creating a Thread Safe Singleton Class with Examples
    In this tutorial we cover many examples of creating thread-safe singleton classes and discuss some of the shortfalls of each and provide some recommendations on best approaches for a fast, efficient and highly concurrent solution.
  • Java Threads and Concurrent Locks with Examples
    In this tutorial we will focus primarily on using the concurrent utilities and how these can make concurrent programming easier for us.

Please Share Us on Social Media

Facebooktwitterredditpinterestlinkedinmail

Leave a Reply

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