Daniel talks about the mutual exclusion and cooperation mechanism of threads in Java concurrent programming principles

Maybe in the implementation principle of the synchronized keyword, you already know that its bottom layer is implemented using the related instructions of Monitor, but the specific details of Monitor are still unclear. This article will let you thoroughly monitor the underlying implementation principles of Monitor.

monitor

A monitor can be thought of as a building with a special room that can only be occupied by one thread. This room contains a lot of data and code.

If a thread wants to occupy a special room (that is, the red area), then first it must wait in the Hallway. The scheduler takes a thread from the Hallway based on some rules (e.g. first in, first out). If the thread is suspended in Hallway for some reason, it will be sent to the waiting room (that is, the blue area), which will be dispatched to the special room after a period of time.

Simply put, a monitor is a device that monitors a live visit to a special room. It enables protected code and data to be accessed by only one thread.

Monitor

In the Java virtual machine, every object and class is associated with a monitor. To implement the monitor's mutex functionality, a lock (sometimes called a mutex) is associated with each object and class. In operating system books, this is called a semaphore, and a mutex is also called a binary semaphore.

If a thread holds a lock on some data, other threads can only acquire the lock until the thread releases the lock. It would be inconvenient if we always need to write a semaphore when doing multithreaded programming. Fortunately, we don't need to do this because the JVM does it for us automatically.

To declare a synchronization area (meaning that data cannot be accessed by more than one thread), Java provides synchronized blocks and synchronized methods. Once code is bound by the synchronized keyword, it is a monitor area. Its lock will be implemented by the JVM later.

Monitor is the main method used to achieve mutual exclusion and cooperation between threads in Java. It can be regarded as a lock of an object or a Class. Every object has and only one monitor. The following figure describes the relationship between threads and Monitor s, as well as the state transition diagram of threads:

Entrance area (Entrt Set): Indicates that the thread requests to acquire the lock of the object through synchronized, but does not get it.

The Owner: Indicates that the thread successfully competed for the object lock.

Waiting area (Wait Set): Indicates that the thread releases the lock of the object through the wait method of the object, and waits to be awakened in the waiting area.

thread state

  • NEW, not started. Does not appear in Dump.

  • RUNNABLE, executed within the virtual machine.

  • BLOCKED, waiting to acquire monitor lock.

  • WATING, waiting indefinitely for another thread to perform a specific action.

  • TIMED_WATING, time-limited waiting for a specific operation of another thread.

  • TERMINATED, exited.

for example:

package com.jiuyan.mountain.test;

import java.util.concurrent.TimeUnit;

/**
 * Hello world!
 *
 */
public class App {

   public static void main(String[] args) throws InterruptedException {
       MyTask task = new MyTask();
       Thread t1 = new Thread(task);
       t1.setName("t1");
       Thread t2 = new Thread(task);
         t2.setName("t2");
        t1.start();
         t2.start();
  }

}

class MyTask implements Runnable {

   private Integer mutex;

   public MyTask() {
       mutex = 1;
   }

   @Override
   public void run() {
       synchronized (mutex) {
         while(true) {
           System.out.println(Thread.currentThread().getName());
           try {
               TimeUnit.SECONDS.sleep(5);
           } catch (InterruptedException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
           }
          }
        }
   }

}

thread status:

"t2" prio=10 tid=0x00007f7b2013a800 nid=0x67fb waiting for monitor entry [0x00007f7b17087000]
 java.lang.Thread.State: BLOCKED (on object monitor)
  at com.jiuyan.mountain.test.MyTask.run(App.java:35)
  - waiting to lock <0x00000007d6b6ddb8> (a java.lang.Integer)
  at java.lang.Thread.run(Thread.java:745)

"t1" prio=10 tid=0x00007f7b20139000 nid=0x67fa waiting on condition [0x00007f7b17188000]
 java.lang.Thread.State: TIMED_WAITING (sleeping)
  at java.lang.Thread.sleep(Native Method)

t1 did not grab the lock, so BLOCKED is displayed. t2 grabs the lock, but is sleeping, so it displays TIMED_WAITING, waiting for a certain condition to wake up.

Remove the sleep code, and the thread state becomes:

"t2" prio=10 tid=0x00007fa0a8102800 nid=0x6a15 waiting for monitor entry [0x00007fa09e37a000]
 java.lang.Thread.State: BLOCKED (on object monitor)
  at com.jiuyan.mountain.test.MyTask.run(App.java:35)
  - waiting to lock <0x0000000784206650> (a java.lang.Integer)
  at java.lang.Thread.run(Thread.java:745)

"t1" prio=10 tid=0x00007fa0a8101000 nid=0x6a14 runnable [0x00007fa09e47b000]
 java.lang.Thread.State: RUNNABLE
  at java.io.FileOutputStream.writeBytes(Native Method)

t1 shows RUNNABLE, which means that it is running. It needs to be explained here. If the thread is querying the database, but the database is deadlocked, although the thread shows that it is running, it does not actually work. For IO-type threads, don’t just use the thread status. Determine whether the work is normal.
Change the code of MyTask a little. After the thread gets the lock, it executes wait, releases the lock, and enters the waiting area.

public void run() {
     synchronized (mutex) {
         if(mutex == 1) {
             try {
                 mutex.wait();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
      }
  }

The thread status is as follows:

"t2" prio=10 tid=0x00007fc5a8112800 nid=0x5a58 in Object.wait() [0x00007fc59b58c000]
 java.lang.Thread.State: WAITING (on object monitor)
  at java.lang.Object.wait(Native Method)

"t1" prio=10 tid=0x00007fc5a8111000 nid=0x5a57 in Object.wait() [0x00007fc59b68d000]
 java.lang.Thread.State: WAITING (on object monitor)
  at java.lang.Object.wait(Native Method)

Both threads show WAITING, this time indefinitely and need to reacquire the lock, so followed by on object monitor.
Another example of deadlock:

package com.jiuyan.mountain.test;

import java.util.concurrent.TimeUnit;

/**
 * Hello world!
 *
 */
public class App {

    public static void main(String[] args) throws InterruptedException {
        MyTask task1 = new MyTask(true);
        MyTask task2 = new MyTask(false);
        Thread t1 = new Thread(task1);
        t1.setName("t1");
        Thread t2 = new Thread(task2);
        t2.setName("t2");
        t1.start();
        t2.start();
    }

}

class MyTask implements Runnable {

    private boolean flag;

    public MyTask(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if(flag) {
            synchronized (Mutex.mutex1) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                synchronized (Mutex.mutex2) {
                    System.out.println("ok");
                }
            }
        } else {
            synchronized (Mutex.mutex2) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                synchronized (Mutex.mutex1) {
                    System.out.println("ok");
                }
            }
        }
    }

}

class Mutex {
   public static Integer mutex1 = 1;
   public static Integer mutex2 = 2;
}  

thread status:

"t2" prio=10 tid=0x00007f5f9c122800 nid=0x3874 waiting for monitor entry [0x00007f5f67efd000]
 java.lang.Thread.State: BLOCKED (on object monitor)
  at com.jiuyan.mountain.test.MyTask.run(App.java:55)
  - waiting to lock <0x00000007d6c45bd8> (a java.lang.Integer)
  - locked <0x00000007d6c45be8> (a java.lang.Integer)
  at java.lang.Thread.run(Thread.java:745)

"t1" prio=10 tid=0x00007f5f9c121000 nid=0x3873 waiting for monitor entry [0x00007f5f67ffe000]
 java.lang.Thread.State: BLOCKED (on object monitor)
  at com.jiuyan.mountain.test.MyTask.run(App.java:43)
  - waiting to lock <0x00000007d6c45be8> (a java.lang.Integer)
  - locked <0x00000007d6c45bd8> (a java.lang.Integer)
  at java.lang.Thread.run(Thread.java:745)

Found one Java-level deadlock:
=============================
"t2":
waiting to lock monitor 0x00007f5f780062c8 (object 0x00000007d6c45bd8, a java.lang.Integer),
which is held by "t1"
"t1":
waiting to lock monitor 0x00007f5f78004ed8 (object 0x00000007d6c45be8, a java.lang.Integer),
which is held by "t2"

This is a bit like the dining philosophers problem. Each thread holds the lock that the other needs, and it can't run.

at last

Reply by private letter Information Receive a summary of Java interview questions from first-line manufacturers + learning thinking guide for each knowledge point + a summary of Java core knowledge points in a 300-page pdf document!

The content of these materials are the knowledge points that the interviewer must ask during the interview. The chapter includes many knowledge points, including basic knowledge, Java collections, JVM, multi-threaded concurrency, spring principles, microservices, Netty and RPC, Kafka , journals, design patterns, Java algorithms, databases, Zookeeper, distributed caches, data structures, and more.

Author: monitor

Tags: Java

Posted by Potatis on Mon, 30 May 2022 11:36:39 +0530