#
Monitors in Java
This tutorial explains the usage of Monitors in Java.
Info
The Monitors are used in Java when some threads are waiting for other threads to let them do something.
For instance, we can think of a table. We have some producers which put products on the table and consumers which takes the products from the table. The consumer takes the product(s) from the table only when the producer(s) notify that one or more products are on the table.
In my example, I start from a simple Spring Boot application created using Spring initializr.
Let's take a look at the following example.
I will create 2 thread. One for simulating a consumer and one for simulating a producer. Here are the classes:
package com.exampe.java;
public class MyThreadProducer extends Thread {
Integer threadNumber;
// object's monitor
final String table;
public MyThreadProducer(Integer threadNumber, String table) {
this.threadNumber = threadNumber;
this.table = table;
}
public void run()
{
System.out.println("Thread " + this.threadNumber + " is running...");
for (int i = 1; i <= 5; i++) {
try {
System.out.println("/Thread " + this.threadNumber + "/ wants to put the products on the table.");
synchronized (table) {
System.out.println("/Thread " + this.threadNumber + "/ PUT the products on the table.");
Thread.sleep(1000);
// You notify that the products could be taken.
table.notify();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
};
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (table) {
table.notify();
}
System.out.println("Thread " + this.threadNumber + " stopped.");
}
}
package com.exampe.java;
public class MyThreadConsumer extends Thread {
Integer threadNumber;
final String table;
public MyThreadConsumer(Integer threadNumber, String table) {
this.threadNumber = threadNumber;
this.table = table;
}
public void run()
{
System.out.println("Thread " + this.threadNumber + " is running...");
System.out.println(table.hashCode());
for (int i = 1; i <= 5; i++) {
try {
System.out.println("/Thread " + this.threadNumber + "/ is waiting for taking the products.");
synchronized (table) {
// You are waiting for the notification which allow you to take the products
table.wait();
System.out.println("/Thread " + this.threadNumber + "/ TOOK the products from the table.");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
};
System.out.println("Thread " + this.threadNumber + " stopped.");
}
}
Here is my main class :
package com.exampe.java;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) throws InterruptedException {
SpringApplication.run(DemoApplication.class, args);
System.out.println("--------------------------------------------");
String table1 = "Table1";
MyThreadProducer t1 = new MyThreadProducer(1, table1);
MyThreadConsumer t2 = new MyThreadConsumer(2, table1);
t1.start();t2.start();
// Waiting for the threads to complete
t1.join(); t2.join();
System.out.println("--------------------------------------------");
System.out.println("End of the execution");
}
}
When I run the code I get (the result is different for each run):
--------------------------------------------
Thread 1 is running...
/Thread 1/ wants to put the products on the table.
/Thread 1/ PUT the products on the table.
Thread 2 is running...
-1797510589
/Thread 2/ is waiting for taking the products.
/Thread 1/ wants to put the products on the table.
/Thread 1/ PUT the products on the table.
/Thread 1/ wants to put the products on the table.
/Thread 2/ TOOK the products from the table.
/Thread 2/ is waiting for taking the products.
/Thread 1/ PUT the products on the table.
/Thread 1/ wants to put the products on the table.
/Thread 1/ PUT the products on the table.
/Thread 1/ wants to put the products on the table.
/Thread 2/ TOOK the products from the table.
/Thread 2/ is waiting for taking the products.
/Thread 1/ PUT the products on the table.
/Thread 2/ TOOK the products from the table.
Thread 1 stopped.
/Thread 2/ is waiting for taking the products.
- As you can see you will always PUT on a table before you will TAKE.
- At the end the consumer is waiting for the producer, but the producer is stopped.
notifyAll()
method simply wakes all threads that are waiting on this object's monitor.
The awakened threads will compete in the usual manner, like any other thread that is trying to synchronize on this object.