[Concurrent programming] synchronized underlying principle: Monitor (Pipeline/Monitor)

Core point of this article

  • synchronized is an unfair lock!
  • There are threads executing, new threads will enter this cxq queue!
  • This release lock analysis uses the default policy (QMode=0): If the EntryList is empty, insert elements from cxq into the EntryList in their original order and wake up the first thread, that is, when the EntryList is empty, subsequent threads acquire the lock first. _ EntryList is not empty, just from _ Wake threads in EntryList.

What exactly is synchronized?

  • synchronized is a JVM built-in lock that is implemented based on the Monitor mechanism.
  • Mutex, the mutex primitive that relies on the underlying operating system.
  • On the surface it is a heavy lock with low performance.
  • In fact, JVM built-in locks have made significant optimizations since version 1.5, such as Lock Coarsening, Lock Elimination, Lightweight Locking, Biased Locking, Adaptive Spinning, and so on, to reduce the overhead of lock operations, and the concurrency performance of built-in locks has been basically the same as Lock.

Monitor (Pipeline/Monitor)

  • Monitor, literally translated as "Monitor", while the operating system domain is generally translated as "Pipeline".
  • Pipelining is the process of managing shared variables and manipulating them to support concurrency.
  • The synchronized keyword and wait(), notify(), notifyAll() are three components of the pipeline technology in Java.

Pipeline Model

  • In the history of pipeline development, there have been three different pipeline models, Hasen model, Hoare model and MESA model.
  • The MESA model is now widely used.

Use of wait(), notify(), and notifyAll()

  • There is a paradigm requirement for using wait: while {wait();}
  • All waiting threads have the same wait condition: use notify().
  • When all waiting threads wake up, do the same: use notify().
  • You only need to wake up one thread: using notify().
  • Use notifyAll() whenever possible at other times.

Java built-in pipeline: synchronized

  • Java references the MESA model, which is streamlined by the language's built-in pipeline (synchronized).
  • In the MESA model, there can be multiple conditional variables and only one conditional variable in the Java language's built-in pipeline.

Implementation of Monitor mechanism in Java

  • Java. The lang.Object class defines the wait(), notify(), notifyAll() methods
  • The implementation of wait(), notify(), notifyAll() depends on the implementation of ObjectMonitor (a mechanism within the JVM).

Main data structure of ObjectMonitor

    _header       = NULL; //Object Header markOop
    _count        = 0;  
    _waiters      = 0,   
    _recursions   = 0;   // synchronized is a reentrant lock, which records the number of times a lock has been reentrant 
    _object       = NULL;  //Storage Lock Object
    _owner        = NULL;  // Identify the thread that owns the monitor (the thread currently acquiring the lock) 
    _WaitSet      = NULL;  // Threads calling wait blocked: a two-way looping list of waiting threads, _ WaitSet is the first node
    _WaitSetLock  = 0 ;    
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ; // With threads executing, newly entered threads enter this queue: multithreaded competing locks are stored first in this one-way chain list (FILO stack structure: unfair!)
    FreeNext      = NULL ;
    _EntryList    = NULL ; //Stores threads that are blocked on entry or reentry (also threads where a race lock fails)
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;

Waiting for wake-up mechanism of synchronized

  • When a lock is acquired, the current thread is inserted into the head of the cxq.
  • Default policy when releasing locks (QMode=0): If EntryList is empty, insert elements from cxq into EntryList in their original order and wake up the first thread, that is, when EntryList is empty, subsequent threads acquire locks first. _ EntryList is not empty, just from _ Wake threads in EntryList.

Execution flow of threads under synchronized: wait mechanism!

  • See the execution of a piece of code
public class SyncQModeDemo {

	public static void main(String[] args) throws InterruptedException {

		SyncQModeDemo demo = new SyncQModeDemo();

		demo.startThreadA();
		// Controlling thread execution time
		Thread.sleep(100);
		demo.startThreadB();
		Thread.sleep(100);
		demo.startThreadC();
	}

	final Object lock = new Object();

	public void startThreadA() {
		new Thread(() -> {
			synchronized (lock) {
				log.debug("A get lock");
				try {
					lock.wait(300);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				log.debug("A release lock");
			}
		}, "thread-A").start();
	}

	public void startThreadB() {
		new Thread(() -> {
			synchronized (lock) {
				try {
					log.debug("B get lock");
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				log.debug("B release lock");
			}
		}, "thread-B").start();
	}

	public void startThreadC() {
		new Thread(() -> {
			synchronized (lock) {

				log.debug("C get lock");
			}
		}, "thread-C").start();
	}
}

results of enforcement

  • A get lock
  • B get lock
  • B release lock
  • A release lock
  • C get lock

Why is this the result?

  • The first thread executes normally: owner is the first thread!
  • The second thread comes in, and because the first one is executing, it will block: owner is the first thread, cxq has the second thread!
  • Suppose at this point the thread calls the wait() method: WaitSet has the first thread and cxq has the second thread! Empty owner!
  • Next time you compete for thread usage, the EntryList is empty, and the thread in cxq executes: WaitSet has the first thread, owner is the second thread!
  • At this time the third thread comes in: cxq has a third thread, WaitSet has a first thread, cxq has a third thread! owner is the second thread!
  • The second thread finishes execution, wakes up other threads, transfers the threads in WaitSet to EntryList: EntryList has the first thread, cxq has the third thread!
  • Next time you compete for thread usage, EntryList has value, wake up the thread directly from EntryList: EntryList has the first thread, owner is the first thread!
  • The first thread finishes execution, wakes up the thread, only cxq has a thread inside, wake up him: owner is the third thread.

Execution flow of threads under synchronized: Competition mechanism

  • See the execution of a piece of code
public class SyncQModeDemo {

	public static void main(String[] args) throws InterruptedException {

		SyncQModeDemo demo = new SyncQModeDemo();

		demo.startThreadA();
		// Controlling thread execution time
		Thread.sleep(100);
		demo.startThreadB();
		Thread.sleep(100);
		demo.startThreadC();
	}

	final Object lock = new Object();

	public void startThreadA() {
		new Thread(() -> {
			synchronized (lock) {
				log.debug("A get lock");
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				log.debug("A release lock");
			}
		}, "thread-A").start();
	}

	public void startThreadB() {
		new Thread(() -> {
			synchronized (lock) {
				try {
					log.debug("B get lock");
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				log.debug("B release lock");
			}
		}, "thread-B").start();
	}

	public void startThreadC() {
		new Thread(() -> {
			synchronized (lock) {

				log.debug("C get lock");
			}
		}, "thread-C").start();
	}
}

results of enforcement

  • A get lock
  • A release lock
  • C get lock
  • B get lock
  • B release lock

Why is this the result?

  • The first thread executes normally: owner is the first thread!
  • The first thread did not finish executing, and the second thread came in: owner is the first thread! There is a second thread in cxq!
  • The first thread did not finish executing, and the third thread came in: owner is the first thread! There is a second thread and a third thread in cxq (queue structure, second thread comes in first, third thread comes in later)!
  • The first thread finishes execution, transferring threads from cxq to EntryList: EntryList has a second thread, a third thread (queue structure, the second thread comes in first, the third thread comes in later)!
  • Wake-up thread: owner is the third thread! There is a second thread in cxq!
  • Thread 3 finished executing: wake up the second thread, execute!

Concluding remarks

  • Get more valuable articles and let's become architects together!
  • Focusing on the Public Number gives you a deep understanding of MySQL
  • Keep an eye on public numbers and learn about concurrent programming consistently and efficiently every day!
  • This public number, no advertisements!!! Update every day!!!

Keywords: Concurrent Programming

Added by Old Novus on Mon, 17 Jan 2022 17:42:14 +0200