Mobile Development 21 min read

Understanding Deadlocks and Their Prevention in Android Development

This article explains the concept of deadlocks, illustrates classic scenarios such as the dining philosophers problem, shows common lock‑order and multi‑object deadlocks with Java code examples, and details Android's WatchDog mechanism and practical techniques for detecting and avoiding deadlocks in mobile applications.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Understanding Deadlocks and Their Prevention in Android Development

Deadlock occurs when two or more threads wait for each other to release resources, causing the system to become unresponsive. It can be formally defined as a set of processes each waiting for an event that only another process in the same set can trigger.

A classic illustration is the dining philosophers problem, where each philosopher holds one fork and waits for the other, creating a circular wait.

In Java, lock‑order deadlocks can arise when threads acquire locks in different sequences. For example:

public class TestDeadLock {
    private final Object lockA = new Object();
    private final Object lockB = new Object();

    public void lockAtoB(){
        synchronized (lockA){
            synchronized (lockB){
                doSomething();
            }
        }
    }
    public void lockBtoA(){
        synchronized (lockB){
            synchronized (lockA){
                doSomething();
            }
        }
    }
    private void doSomething(){
        System.out.println("doSomething");
    }
}

If one thread calls lockAtoB() while another calls lockBtoA() , a deadlock may occur because the locks are acquired in opposite order.

A more subtle case involves multiple objects, such as a warehouse transfer method that locks two IStore instances. Even though the method locks them in a consistent order, callers may pass the arguments in opposite order, leading to a deadlock. One mitigation is to order locks by their System.identityHashCode() values:

private static final Object extraLock = new Object();
public void transportGoods(IStore from, IStore to, int count){
    int fromHash = System.identityHashCode(from);
    int toHash = System.identityHashCode(to);
    if (fromHash > toHash) {
        synchronized (from){
            synchronized (to){
                transportGoodsInternal(from, to, count);
            }
        }
    } else if (fromHash < toHash) {
        synchronized (to){
            synchronized (from){
                transportGoodsInternal(from, to, count);
            }
        }
    } else {
        synchronized (extraLock){
            synchronized (from){
                synchronized (to){
                    transportGoodsInternal(from, to, count);
                }
            }
        }
    }
}

public void transportGoodsInternal(IStore from, IStore to, int count){
    from.outCome(count);
    to.inCome(count);
}

Another deadlock pattern is thread‑starvation deadlock, where tasks submitted to a thread pool wait for each other, causing the pool to become permanently blocked.

private ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,0,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>());

public void start() throws Exception {
    Callable
second = () -> { Thread.sleep(100); return "second"; };
    Callable
first = () -> {
        Future
f = executor.submit(second);
        return "first:" + f.get();
    };
    for (int i=0;i<5;i++) executor.submit(first);
}

Android provides a built‑in WatchDog in the framework layer to monitor critical system services. The WatchDog runs in a loop, periodically scheduling HandlerChecker objects for each important thread (main, UI, I/O, display, etc.). It uses MessageQueue.isPolling() to quickly determine if a looper is still active, and if a thread exceeds a timeout, it dumps stack traces, triggers sysrq commands, and may kill the system_server process to force a restart.

Developers can also detect deadlocks in their apps by using Android Studio’s thread‑dump feature or by manually sending a SIGQUIT signal to generate an ANR trace file. The trace contains stack information that reveals which threads are waiting on which locks.

In summary, understanding deadlock fundamentals, applying consistent lock ordering (e.g., via identity hash codes), reducing lock granularity, and leveraging Android’s WatchDog and debugging tools are essential practices to prevent and diagnose deadlocks in mobile applications.

JavaAndroidConcurrencydeadlockMultithreadingwatchdog
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

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.