In this post, we will explore how to run two threads synchronously in Java, where:
Thread 1 prints:
DesignThread 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
ReentrantLockis a more flexible and advanced lock mechanism compared tosynchronizedblocks.It provides explicit lock and unlock operations, which gives you finer control over thread synchronization.
2. Condition
The
Conditioninterface allows threads to wait for a specific condition to become true.It works with a
ReentrantLockto provide more advanced thread communication compared towait()andnotify().
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
Shared Resource:
The
PrintControllerclass acts as a shared resource between the threads.It uses a
ReentrantLockto control access to critical sections.
Thread Coordination:
Conditionobjects (designConditionandalgorithmCondition) are used to alternate between the threads.The
isDesignTurnflag tracks which thread's turn it is to print.
Execution Flow:
Thread 1 prints "Design" only when
isDesignTurnistrue. After printing, it sets the flag tofalseand signals Thread 2.Thread 2 prints "Algorithm Diaries" when
isDesignTurnisfalse. After printing, it sets the flag totrueand signals Thread 1.
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 DiariesKey Takeaways
Using
ReentrantLockandConditionprovides 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!