Thursday, 16 January 2025

Running Two Threads Using ReentrantLock and Condition in Java

 In this post, we will explore how to run two threads synchronously in Java, where:

  • Thread 1 prints: Design

  • Thread 2 prints: Algorithm Diaries

To achieve this, we will use Java's ReentrantLock and Condition classes to handle synchronization between the two threads. This approach ensures proper coordination, allowing the threads to alternate their prints as intended.


Understanding the Tools

1. ReentrantLock

  • A ReentrantLock is a more flexible and advanced lock mechanism compared to synchronized blocks.

  • It provides explicit lock and unlock operations, which gives you finer control over thread synchronization.

2. Condition

  • The Condition interface allows threads to wait for a specific condition to become true.

  • It works with a ReentrantLock to provide more advanced thread communication compared to wait() and notify().


Implementation

Below is the implementation of two threads using ReentrantLock and Condition to print "Design" and "Algorithm Diaries" alternately.

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

public class ThreadSynchronization {

    public static void main(String[] args) {
        PrintController controller = new PrintController();

        // Thread 1: Prints "Design"
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                controller.printDesign();
            }
        });

        // Thread 2: Prints "Algorithm Diaries"
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                controller.printAlgorithmDiaries();
            }
        });

        thread1.start();
        thread2.start();
    }
}

class PrintController {
    private final Lock lock = new ReentrantLock();
    private final Condition designCondition = lock.newCondition();
    private final Condition algorithmCondition = lock.newCondition();
    private boolean isDesignTurn = true;

    public void printDesign() {
        lock.lock();
        try {
            while (!isDesignTurn) {
                designCondition.await(); // Wait until it's Design's turn
            }
            System.out.println("Design");
            isDesignTurn = false;
            algorithmCondition.signal(); // Notify Algorithm Diaries thread
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }

    public void printAlgorithmDiaries() {
        lock.lock();
        try {
            while (isDesignTurn) {
                algorithmCondition.await(); // Wait until it's Algorithm Diaries' turn
            }
            System.out.println("Algorithm Diaries");
            isDesignTurn = true;
            designCondition.signal(); // Notify Design thread
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }
}

Explanation of the Code

  1. Shared Resource:

    • The PrintController class acts as a shared resource between the threads.

    • It uses a ReentrantLock to control access to critical sections.

  2. Thread Coordination:

    • Condition objects (designCondition and algorithmCondition) are used to alternate between the threads.

    • The isDesignTurn flag tracks which thread's turn it is to print.

  3. Execution Flow:

    • Thread 1 prints "Design" only when isDesignTurn is true. After printing, it sets the flag to false and signals Thread 2.

    • Thread 2 prints "Algorithm Diaries" when isDesignTurn is false. After printing, it sets the flag to true and signals Thread 1.

  4. Thread Safety:

    • Both threads acquire the lock before checking or modifying shared variables and release it afterward, ensuring thread safety.


Output

When you run the program, you will see the following output:

Design
Algorithm Diaries
Design
Algorithm Diaries
Design
Algorithm Diaries
Design
Algorithm Diaries
Design
Algorithm Diaries

Key Takeaways

  • Using ReentrantLock and Condition provides precise control over thread execution.

  • The approach allows you to alternate between threads effectively without busy waiting.

  • This technique is useful for complex thread synchronization scenarios.

Feel free to experiment with the code and extend it to more threads or alternate behaviors. Happy coding!