Printing odd and even numbers in sequence using 2 threads is a use case of synchronization and inter thread communication.
Problem Statement
We have to print odd and even numbers in sequence using 2 threads, that means one thread is printing only odd numbers and another one is printing even numbers only.
Solution
Let’s say we have a common resource called Printer
and 2 threads called OddPrinter
and EvenPrinter
are trying to access it. In order to make them print in sequence, we need to use below 2 concepts.
- Resource synchronization: Whenever multiple threads are accessing a common resource, we need to synchronize that resource so that it can be accessed by 1 thread at a time and data discrepancy can be avoided.
- Inter thread communication: Whenever we need communication between threads, we use this concept. In our example, to make them print in sequence, we need to make both the printers/threads communicate with each other. We achieve this by using
wait()
,notify()
andnotifyAll()
methods in Java.
Implementation
To implement the solution, we need to break it down into 4 pieces which are the following.
- CommonPrinter
- OddPrinter
- EvenPrinter
- Main class to run the code
CommonPrinter
This class contains logic. The Printer object is the resource that gets accessed by both the threads hence it is synchronized.
It offers 2 methods i.e. printEven()
and printOdd()
that get called by EvenPrinter and OddPrinter threads respectively.
public class Printer {
private final int max;
public Printer(int max) {
this.max = max;
}
/**
* This method gets called from EvenPrinter and it only prints Even values.
*
* @throws InterruptedException
*/
public void printEven() throws InterruptedException {
synchronized (this) { //Since this is a common resource, it needs to be synchronized.
for (int i = 0; i < max; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + "-" + i); //Even values can be printed directly since it is an EvenPrinter, no magic here.
notify(); //Notify the another thread (OddPrinter) so that that can continue.
} else {
wait(); //Since it is an EvenPrinter, it needs to wait at odd values.
}
}
}
}
/**
* This method gets called from OddPrinter and it prints only Odd values.
*
* @throws InterruptedException
*/
public void printOdd() throws InterruptedException {
synchronized (this) {
for (int i = 0; i < max; i++) {
if (i % 2 == 0) {
wait(); //Waiting at even values since it is an OddPrinter.
} else {
System.out.println(Thread.currentThread().getName() + "-" + i); //Printing odd values directly since it is an Odd printer, no magic here.
notify(); //Notify the EvenPrinter so that that can continue.
}
}
}
}
}
OddPrinter
OddPrinter is a Runnable task that gets executed via a Thread. We can also make OddPrinter a thread by extending the Thread class but for simplicity we have made it a Runnable task.
It internally calls the printOdd() of the Printer class.
public class OddPrinter implements Runnable {
private final Printer printer;
public OddPrinter(Printer printer) {
this.printer = printer;
}
@Override
public void run() {
try {
printer.printOdd();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
EvenPrinter
EvenPrinter is also a Runnable task that gets run by a Thread. It calls the printEven() of the Printer class to print even numbers.
public class EvenPrinter implements Runnable {
private final Printer printer;
public EvenPrinter(Printer printer) {
this.printer = printer;
}
@Override
public void run() {
try {
printer.printEven();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Main Class
In the main class we create an object of the Printer
class which is going to be used by OddPrinter
and EvenPrinter
.
The Printer class takes a parameter called max that takes a number till what you want to print the numbers.
public class Main {
public static void main(String[] args) {
Printer printer = new Printer(10);
Thread odd = new Thread(new OddPrinter(printer)); //Common printer to be used by OddPrinter
odd.setName("ODD");
Thread even = new Thread(new EvenPrinter(printer)); //Common printer to be used by EvenPrinter
even.setName("EVEN");
odd.start();
even.start();
}
}
Output
EVEN-0
ODD-1
EVEN-2
ODD-3
EVEN-4
ODD-5
EVEN-6
ODD-7
EVEN-8
ODD-9