Home Python C Language C ++ HTML 5 CSS Javascript Java Kotlin SQL DJango Bootstrap React.js R C# PHP ASP.Net Numpy Dart Pandas Digital Marketing

Identifying and Resolving Race Conditions and Deadlocks


Concurrency in Java provides significant benefits in terms of performance and responsiveness but can lead to complex issues like race conditions and deadlocks. This article explains how to identify and resolve these issues step by step with practical examples.

What Are Race Conditions?

A race condition occurs when multiple threads access shared resources concurrently, and the program's behavior depends on the timing of their execution. This can lead to unpredictable and incorrect results.

Example of a Race Condition

    public class RaceConditionExample {
        private int counter = 0;

        public void increment() {
            counter++;
        }

        public static void main(String[] args) {
            RaceConditionExample example = new RaceConditionExample();

            Runnable task = () -> {
                for (int i = 0; i < 1000; i++) {
                    example.increment();
                }
            };

            Thread t1 = new Thread(task);
            Thread t2 = new Thread(task);

            t1.start();
            t2.start();

            try {
                t1.join();
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("Final Counter Value: " + example.counter);
        }
    }
        

Problem: The counter value is incorrect due to concurrent modifications by multiple threads.

Resolving Race Conditions

To resolve race conditions, use synchronization mechanisms like synchronized blocks or locks.

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;

    public class RaceConditionResolved {
        private int counter = 0;
        private final Lock lock = new ReentrantLock();

        public void increment() {
            lock.lock();
            try {
                counter++;
            } finally {
                lock.unlock();
            }
        }

        public static void main(String[] args) {
            RaceConditionResolved example = new RaceConditionResolved();

            Runnable task = () -> {
                for (int i = 0; i < 1000; i++) {
                    example.increment();
                }
            };

            Thread t1 = new Thread(task);
            Thread t2 = new Thread(task);

            t1.start();
            t2.start();

            try {
                t1.join();
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("Final Counter Value: " + example.counter);
        }
    }
        

What Are Deadlocks?

A deadlock occurs when two or more threads are waiting for each other to release resources, resulting in a situation where none of the threads can proceed.

Example of a Deadlock

    public class DeadlockExample {
        private final Object lock1 = new Object();
        private final Object lock2 = new Object();

        public void method1() {
            synchronized (lock1) {
                System.out.println(Thread.currentThread().getName() + " locked lock1");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println(Thread.currentThread().getName() + " locked lock2");
                }
            }
        }

        public void method2() {
            synchronized (lock2) {
                System.out.println(Thread.currentThread().getName() + " locked lock2");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println(Thread.currentThread().getName() + " locked lock1");
                }
            }
        }

        public static void main(String[] args) {
            DeadlockExample example = new DeadlockExample();

            Thread t1 = new Thread(example::method1);
            Thread t2 = new Thread(example::method2);

            t1.start();
            t2.start();
        }
    }
        

Problem: Thread 1 locks lock1 and waits for lock2, while Thread 2 locks lock2 and waits for lock1, causing a deadlock.

Resolving Deadlocks

To avoid deadlocks, follow these techniques:

Deadlock-Free Code

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;

    public class DeadlockResolved {
        private final Lock lock1 = new ReentrantLock();
        private final Lock lock2 = new ReentrantLock();

        public void method1() {
            if (lock1.tryLock()) {
                try {
                    if (lock2.tryLock()) {
                        try {
                            System.out.println(Thread.currentThread().getName() + " acquired both locks");
                        } finally {
                            lock2.unlock();
                        }
                    }
                } finally {
                    lock1.unlock();
                }
            } else {
                System.out.println(Thread.currentThread().getName() + " could not acquire locks");
            }
        }

        public static void main(String[] args) {
            DeadlockResolved example = new DeadlockResolved();

            Thread t1 = new Thread(example::method1);
            Thread t2 = new Thread(example::method1);

            t1.start();
            t2.start();
        }
    }
        

Conclusion

Race conditions and deadlocks are common concurrency issues that can compromise the correctness and performance of multithreaded applications. Identifying these problems through careful debugging and using synchronization techniques, higher-level concurrency utilities, or lock ordering can help you build robust applications.



Advertisement





Q3 Schools : India


Online Complier

HTML 5

Python

java

C++

C

JavaScript

Website Development

HTML

CSS

JavaScript

Python

SQL

Campus Learning

C

C#

java