If you took a college operating system course, you probably remember that monitors are an important concept for synchronization in operating systems. It is also used for Java synchronization. This article uses analogies to explain the basic concept of a monitor.

1. What is monitor?

A monitor can be considered a building containing special rooms. A special room can only hold one client (thread) at a time. Rooms usually contain some data and code.

If a customer wants to occupy a Special Room, they must first enter Entry Set and wait. The scheduler will select one based on certain criteria, such as FIFO. If he is suspended for any reason, he will be sent to the Wait Room and arranged to enter the Special Room later. As the picture above shows, the Building has three rooms.

In short, a monitor is a tool that monitors thread access to a particular room. It ensures that only one thread can access protected data or code.

2. How to use Java?

In the Java virtual machine, every object and class is logically associated with the monitor.

To implement the mutex capability of the monitor, a lock (sometimes called a mutex) is associated with each object and class. This is called a semaphore in operating system books, and the mutex is a binary semaphore.

If one thread has a lock on some data, other threads can acquire the lock only if the thread that owns it releases the lock. It would be inconvenient if you always had to write semaphores while doing multithreaded programming. Fortunately, since the JVM does this automatically for us, we don’t need to do this.

To declare a monitoring region, which means that multiple threads cannot access the data, Java provides synchronized statements and synchronized methods. Once the synchronized keyword is embedded in your code, it is a monitoring area. The JVM automatically implements locking in the background.

3. What part of Java synchronization code is a monitor?

We know that each object/class is associated with a Monitor. I think it’s fair to say that each object has a monitor, because each object can have its own key parts and be able to monitor thread sequences. To enable collaboration between threads, java.lang.object in Java provides wait() and notify() to suspend a thread and wake up another thread that is waiting on the Object. In addition, there are 3 other versions:

wait(long timeout, int nanos)
wait(long timeout) notified by other threads or notified by timeout. 
notify(all)
Copy the code

These methods can only be called in synchronized statements or synchronized methods (to get the monitor of the object). The reason is that if a method does not need to be mutually exclusive and does not need to monitor or collaborate with threads, each thread can freely access the method.

In short, when we call wait(), this forces the current thread to wait until some other thread calls notify() or notifyAll() on the same object. To do this, the current thread must have a monitor for the object. According to Javadocs, this can happen when:

  • We have performed a synchronous instance method for the given object
  • We have synchronized the body of the block on the given object
  • Static methods that perform synchronization on objects of type Class

Note that only one active thread can own an object’s monitor at a time.

The thread that is awakened by notify()/notiryAll() is not guaranteed to get the monitor of the object immediately, and must compete with other threads to lock the object. In the process of competition, the thread has no privilege or disadvantage, and all beings are equal.

4. Code practice

Requirement: from A to Z, there are 26 letters, through two threads one by one print the 26 letters.

/** * print one by one, Use two threads. * * @author [email protected] * @version 1.0 * @date 2020-04-18 11:18 */ public class ThreadSchedulingTest { public volatile AtomicInteger readIndex = new AtomicInteger(0); public List<String> stringList = Arrays.asList("ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("")); public ReentrantLock lock = new ReentrantLock(true); */ @test public void testThreadScheduling() {Thread thread1 = generateThread("Thread-0"); Thread thread2 = generateThread("Thread--1"); thread1.start(); thread2.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("thread1 in main method, state=" + thread1.getState().name()); System.out.println("thread2 in main method, state=" + thread2.getState().name()); } public Thread generateThread(String threadName) { Thread thread = new Thread(() -> { synchronized (stringList) { String name = Thread.currentThread().getName(); while (readIndex.get() < stringList.size()) { System.out.println(name + " | " + stringList.get(readIndex.get())); readIndex.getAndIncrement(); try { stringList.notify(); stringList.wait(); } catch (InterruptedException e) { e.printStackTrace(); }}}}); thread.setName(threadName); return thread; }}Copy the code

Test case output:

Thread-0 | A Thread--1 | B Thread-0 | C Thread--1 | D Thread-0 | E Thread--1 | F Thread-0 | G Thread--1 | H Thread-0 | I  Thread--1 | J Thread-0 | K Thread--1 | L Thread-0 | M Thread--1 | N Thread-0 | O Thread--1 | P Thread-0 | Q Thread--1 |  R Thread-0 | S Thread--1 | T Thread-0 | U Thread--1 | V Thread-0 | W Thread--1 | X Thread-0 | Y Thread--1 | ZCopy the code
  • Code address: code Github address

5. Refer to the article

  • www.programcreek.com/2011/12/mon…
  • www.baeldung.com/java-wait-n…