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 tosynchronized
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 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
PrintController
class acts as a shared resource between the threads.It uses a
ReentrantLock
to control access to critical sections.
Thread Coordination:
Condition
objects (designCondition
andalgorithmCondition
) are used to alternate between the threads.The
isDesignTurn
flag tracks which thread's turn it is to print.
Execution Flow:
Thread 1 prints "Design" only when
isDesignTurn
istrue
. After printing, it sets the flag tofalse
and signals Thread 2.Thread 2 prints "Algorithm Diaries" when
isDesignTurn
isfalse
. After printing, it sets the flag totrue
and 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 Diaries
Key Takeaways
Using
ReentrantLock
andCondition
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!