Swiftorial Logo
Home
Swift Lessons
Matchups
CodeSnaps
Tutorials
Career
Resources

Deadlock and Race Conditions in Java

1. Understanding Deadlock

Deadlock is a concurrency issue that occurs when two or more threads are blocked forever, each waiting for the other to release a resource. In Java, this often happens when multiple threads try to acquire locks on shared resources.

Key Concepts

  • Mutual Exclusion: Resources cannot be shared.
  • Hold and Wait: Threads holding resources are waiting for others.
  • No Preemption: Resources cannot be forcibly taken from threads.
  • Circular Wait: A circular chain of threads exists, each waiting for a resource held by the next thread.
Note: Deadlock can severely impact performance and system reliability.

2. Race Conditions

A race condition occurs when the outcome of a program depends on the sequence or timing of uncontrollable events, such as the order in which threads are scheduled. This can lead to inconsistent or unexpected results.

Key Concepts

  • Shared Data: When multiple threads access shared data without proper synchronization.
  • Thread Safety: Ensuring that shared data is accessed and modified safely by multiple threads.
Tip: Always synchronize access to shared resources to prevent race conditions.

3. Code Examples

Deadlock Example


class Resource {
    synchronized void methodA(Resource resource) {
        System.out.println(Thread.currentThread().getName() + " acquired lock on " + this);
        resource.methodB(this);
    }

    synchronized void methodB(Resource resource) {
        System.out.println(Thread.currentThread().getName() + " acquired lock on " + this);
    }
}

public class DeadlockExample {
    public static void main(String[] args) {
        final Resource resource1 = new Resource();
        final Resource resource2 = new Resource();

        new Thread(() -> resource1.methodA(resource2)).start();
        new Thread(() -> resource2.methodA(resource1)).start();
    }
}
            

Race Condition Example


class Counter {
    private int count = 0;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

public class RaceConditionExample {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) counter.increment();
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) counter.increment();
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Final count: " + counter.getCount());
    }
}
            

4. Best Practices

  • Always use synchronized blocks or methods when accessing shared resources.
  • Use higher-level concurrency utilities from the java.util.concurrent package.
  • Avoid nested locks when possible to reduce the chance of deadlocks.
  • Use timeout mechanisms for acquiring locks to prevent indefinite waiting.
  • Regularly analyze and test your code for concurrency issues.

5. FAQ

What is a deadlock in Java?

A deadlock occurs when two or more threads are blocked forever, each waiting for the other to release a resource.

How can I prevent race conditions?

Use synchronization mechanisms, such as synchronized blocks or locks, to ensure that only one thread can access shared data at a time.

What are the symptoms of a deadlock?

Symptoms include threads that are blocked and not making progress, high CPU usage, and eventually application hang.