https://medium.com/@das.subhankar90/java-program-for-printing-sequence-using-3-threads-5ffb2dd56bee
Method-1: Using Conditional Queues
class PrintSequenceWorker implements Runnable {
private final int remainder;
private final int maxNumber;
private final int numOfThreads;
// keep it static to share between threads
private static final Object lock = new Object();
// keep it static to share the current number being printed
// no need for volatile since it is guarded by `lock`
private static int number = 0;
PrintSequenceWorker(int remainder, int maxNumber, int numOfThreads) {
this.remainder = remainder;
this.maxNumber = maxNumber;
this.numOfThreads = numOfThreads;
}
@Override
public void run() {
synchronized (lock) {
while (true) {
while (number % numOfThreads != remainder) {
try {
// !imp: when number is overshoot then
// we must not wait again if lock is held
if (number > maxNumber) {
break;
}
lock.wait();
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
// determine why the loop ended
// if we overshoot the number then we can stop
if (number > maxNumber) {
break;
}
System.out.println(Thread.currentThread().getName() + ": " + number);
number++;
lock.notifyAll();
}
}
}
}- Driver
public class ConcurrentNumbersSequence {
public static void main(String[] args) throws InterruptedException {
// inputs:
// * numOfThreads
// * maxNumberToPrint assuming start happens from 0
int maxNumberToPrint = 20;
int numOfThreads = 5;
Thread[] threads = new Thread[numOfThreads];
for (int i = 0; i < numOfThreads; i++) {
threads[i] = new Thread(new PrintSequenceWorker(i, maxNumberToPrint, numOfThreads));
threads[i].start();
}
// we have created non-daemon threads so the
// program will continue to execute till all threads die
// But we can still wait for all the threads to stop explicitly
for (int i = 0; i < numOfThreads; i++) {
threads[i].join();
}
System.out.println("Program ended");
}
}Notes
number = 21, assuming all the waiting threads have been notified in the previous step. Execution will start from the wait statement:
BLOCKED Thread-0
BLOCKED Thread-1 -- suppose this holds the lock
BLOCKED Thread-2
// next
Thread-1 gets its condition fulfilled (number % 3 == remainder), but due to overshoot we stop execution without incrementing the number (second if-condition)
this breaks while loop, releases the lock and terminates Thread-2
BLOCKED Thread-0 -- suppose this will hold the lock next
TERMINATED Thread-1
BLOCKED Thread-2
// next
Thread-0 will still wait since number was not incremented and the condition will never be fulfilled, hence execution never complete and we reach livelock problem
WAITING Thread-0
TERMINATED Thread-1
BLOCKED Thread-2