SoFunction
Updated on 2025-05-07

Five ways to avoid Java deadlocks, summarize

Preface

Deadlock refers to the situation where multiple threads hold the resources needed by each other, causing all threads to be unable to continue execution. In Java, deadlocks can be avoided by the following methods:

One reason for deadlock:

  • A resource can only be used by one thread at a time

  • A thread does not release the occupied resources while blocking and waiting for a resource.

  • The resources that a thread has obtained cannot be forcibly deprived before they are used.

  • If the thread forms a loop waiting resource relationship with head and tail connection

These are the four conditions that must be met to cause a deadlock. If you want to avoid deadlock, you only need to be dissatisfied with one of the conditions. The first three conditions are the conditions that the lock must meet, so to avoid deadlock, the fourth condition needs to be broken and there is no loop waiting for the lock.

During development:

  • Pay attention to the locking order and ensure that each thread locks in the same order

  • Pay attention to the locking time limit, you can set a timeout time for the

  • Pay attention to deadlock checking, which is a prevention mechanism to ensure that deadlocks are discovered and resolved at the first time

1. Four necessary conditions for deadlock generation

To avoid deadlocks, you must first understand the conditions for deadlocks to occur (Must be satisfied):

  • Mutual Exclusion Conditions: Resources can only be occupied by one thread at a time.

  • Take possession and wait: While the thread holds the resource, it waits for other resources.

  • Not to be seized: The resources held by threads cannot be forcibly taken away by other threads.

  • Loop waiting: Multiple threads form ring waiting chains (A, etc. B, B, C, C, etc. A).

As long as you destroy any of the conditions, you can avoid deadlocks!

2. 5 ways to avoid deadlocks

Method 1: Acquire the lock in a fixed order (break the loop waiting)

Core idea: All threads apply for locks in the same order to avoid circular dependencies.

✅ Correct example

public void transfer(Account from, Account to, int amount) {
    // Regulations: First lock an account with a small id, then lock an account with a large id    Account first = () < () ? from : to;
    Account second = () < () ? to : from;

    synchronized (first) {
        synchronized (second) {
            if (() >= amount) {
                (amount);
                (amount);
            }
        }
    }
}

Why can deadlock be avoided?All threads pressidSequential locking will not appearA lock 1 → 2andB Lock 2 → et al. 1Scenario.

Method 2: Use tryLock + timeout (destroy possession and wait)

Core idea: If you cannot get the lock, release the lock you already hold to avoid infinite waiting.

✅ Correct example (ReentrantLock)

public boolean transfer(Account from, Account to, int amount, long timeout) throws InterruptedException {
    long startTime = ();
    while (true) {
        if (()) { // Try to get the first lock            try {
                if (()) { // Try to get the second lock                    try {
                        if (() >= amount) {
                            (amount);
                            (amount);
                            return true;
                        }
                    } finally {
                        ();
                    }
                }
            } finally {
                (); // Release the first lock            }
        }
        // Timeout check        if (() - startTime >= timeout) {
            return false;
        }
        (100); // Avoid the CPU busy waiting    }
}

advantage

  • There will be no infinite waiting, and you can try again or roll back after the timeout.

  • Suitable for high concurrency scenarios (such as payment systems).

Method 3: Apply for all resources at once (destroy possession and wait)

Core idea: Use a global lock to apply for all required resources at once.

✅ Correct example

public class AccountManager {
    private static final Object globalLock = new Object();

    public void transfer(Account from, Account to, int amount) {
        synchronized (globalLock) { // Global lock protects all accounts            if (() >= amount) {
                (amount);
                (amount);
            }
        }
    }
}

shortcoming: The concurrency is reduced (all transfer operations are serialized).

Method 4: Use Lock instead of synchronized (more flexible control)

ReentrantLockComparesynchronizedMore flexible and can avoid deadlocks:

Lock lock1 = new ReentrantLock();
Lock lock2 = new ReentrantLock();

public void methodA() {
    boolean gotLock1 = false;
    boolean gotLock2 = false;
    try {
        gotLock1 = (1, );
        gotLock2 = (1, );
        if (gotLock1 && gotLock2) {
            // Execute business logic        }
    } catch (InterruptedException e) {
        ().interrupt();
    } finally {
        if (gotLock1) ();
        if (gotLock2) ();
    }
}

Method 5: Detection and recovery (allows deadlocks to occur, but can be automatically restored)

Suitable for complex systems(such as databases, distributed systems):

1. Detect deadlock

useThreadMXBeanFind deadlock:

ThreadMXBean bean = ();
long[] threadIds = ();
if (threadIds != null) {
    ("Deadlock detected!");
}

2. Recovery strategy

Forced terminate a thread (such as())。

Roll back the transaction (database scenario).

3. Deadlock case analysis

❌ Error code (which will cause deadlock)

public void transfer(Account from, Account to, int amount) {
    synchronized (from) {  // Thread 1: Lock from → etc to        synchronized (to) { // Thread 2: Lock to → etc from            if (() >= amount) {
                (amount);
                (amount);
            }
        }
    }
}

Deadlock scene

  • Thread 1:transfer(accountA, accountB, 100)

  • Thread 2:transfer(accountB, accountA, 200)

  • result: Wait for each other, deadlock!

4. Summary

method Applicable scenarios advantage shortcoming
Locking in fixed order Simple lock dependency Simple implementation Global sorting rules are required
tryLock + Timeout High concurrency system Avoid infinite waiting High code complexity
Global lock Low concurrency scenarios Absolutely safe Poor performance (serialization)
Lock replace synchronized Need for finer granular control Flexible (interruptible, timeout) Need to release the lock manually
Detection and recovery Database, distributed system Suitable for complex scenarios Implementation complex

Best Practices

  • Try to use tryLock + timeout(recommendReentrantLock)。

  • If synchronized must be used, add locks in a fixed order

  • Avoid nested locks(likesynchronizedAdjust againsynchronizedmethod).

  • Use tools to detect deadlocks(likejstackThreadMXBean)。

This is the end of this article about five methods to avoid Java deadlock. For more related content on Java deadlock avoidance, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!