Fundamentals 7 min read

Understanding Java Deadlocks: Causes, Examples, and Prevention Strategies

This article explains the concept of deadlocks in concurrent programming, outlines the four necessary conditions, examines common causes in Java, provides a runnable code example, and presents practical prevention, avoidance, detection, and recovery techniques to keep multithreaded applications running smoothly.

Architecture & Thinking
Architecture & Thinking
Architecture & Thinking
Understanding Java Deadlocks: Causes, Examples, and Prevention Strategies

1. Concept of Deadlock

Deadlock is a critical concept in operating systems and concurrent programming where two or more processes are each waiting for resources held by the other, causing a standstill.

2. The Essence of Deadlock

In Java, deadlocks typically occur in multithreaded environments when threads compete for resources and wait on each other.

For example, Thread A holds lock1 and waits for lock2, while Thread B holds lock2 and waits for lock1, leading to a deadlock.

Deadlock diagram
Deadlock diagram

The four necessary conditions for a deadlock are:

Mutual Exclusion Resources cannot be shared and can be used by only one process at a time.

Hold and Wait A process holding resources may request additional ones.

No Pre-emption Allocated resources cannot be forcibly taken away from a process.

Circular Wait Processes form a circular chain, each waiting for a resource held by the next.

In simple terms, each party waits for the other's resource without yielding.

3. Causes of Deadlock

Deadlocks arise from:

Resource Competition Multiple threads share limited resources such as files, memory, or database locks, leading to circular dependencies.

Inconsistent Lock Acquisition Order Threads acquiring locks in different orders can cause each to wait for the other.

Exceptions or Logic Errors Errors that prevent a thread from releasing a lock can block other threads.

3.1 Java Deadlock Example

The following Java code demonstrates a deadlock scenario:

<code>public class DeadlockExample {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread 1: Holding lock 1...");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                System.out.println("Thread 1: Waiting for lock 2...");
                synchronized (lock2) {
                    System.out.println("Thread 1: Holding lock 1 and lock 2...");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread 2: Holding lock 2...");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                System.out.println("Thread 2: Waiting for lock 1...");
                synchronized (lock1) {
                    System.out.println("Thread 2: Holding lock 2 and lock 1...");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}
</code>

When both threads run, each holds one lock and waits for the other, causing a deadlock.

4. Solutions to Deadlock

Deadlock handling can be divided into prevention, avoidance, detection, and recovery.

4.1 Prevention

Break mutual exclusion by allowing concurrent access to some resources.

Break no‑preemption by permitting forced resource takeover.

Break hold‑and‑wait by using pre‑allocation strategies.

Break circular wait by enforcing a fixed ordering of resource acquisition.

Prevention illustration
Prevention illustration

4.2 Avoidance

Apply algorithms such as the Banker's algorithm to check the safety of resource allocation before granting requests.

Avoidance illustration
Avoidance illustration

4.3 Detection and Recovery

Use resource allocation graphs or similar methods to detect deadlocks.

Once detected, resolve by resource preemption, terminating processes, or rolling back transactions.

Javaconcurrencydeadlockresource managementSynchronizationmultithreading
Architecture & Thinking
Written by

Architecture & Thinking

🍭 Frontline tech director and chief architect at top-tier companies 🥝 Years of deep experience in internet, e‑commerce, social, and finance sectors 🌾 Committed to publishing high‑quality articles covering core technologies of leading internet firms, application architecture, and AI breakthroughs.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.