[concurrent programming] detailed explanation of synchronized bias lock, lightweight lock and heavyweight lock

Lock status corresponding to memory layout

Let's start with the conclusion of the change of lock state

Bias lock

  • Bias lock is an optimization method for locking operation.
  • In most cases, locks not only do not have multi-threaded competition, but are always obtained by the same thread many times. Therefore, biased locks are introduced to eliminate the cost of lock reentry (CAS operation) without competition.
  • For occasions without lock competition, biased lock has a good optimization effect.
  • The JVM enables the bias lock mode: jdk6 then it is enabled by default
  • The Thread Id in the Mark Word of the newly created object is 0, indicating that it is biased but not biased to any thread at this time, which is also called anonymous biased.

Bias lock delay bias

  • When the HotSpot virtual machine is started, the bias lock mode is turned on. By default, it is after 4s.
  • To reduce initialization time, the JVM delays loading biased locks by default.
//Closing delay opening bias lock
-XX:BiasedLockingStartupDelay=0
//No deflection lock
-XX:-UseBiasedLocking 

  • The code in the figure above can verify the change from no lock to biased lock (4 seconds)

Biased lock is always biased lock when there is no competition

public static void main(String[] args) throws InterruptedException {
		log.debug(Thread.currentThread().getName() + "The initial state...\n"
				+ ClassLayout.parseInstance(new Object()).toPrintable());
		// The HotSpot virtual machine will not turn on the bias lock mode for each newly created object until it has a 4s delay after startup
		Thread.sleep(4000);
		Object obj = new Object();

		new Thread(new Runnable() {
			@Override
			public void run() {
				log.debug(
						Thread.currentThread().getName() + "Start execution, prepare to acquire lock...\n" + ClassLayout.parseInstance(obj).toPrintable());
				synchronized (obj) {
					log.debug(Thread.currentThread().getName() + "Lock acquisition in progress...\n"
							+ ClassLayout.parseInstance(obj).toPrintable());
				}
				log.debug(Thread.currentThread().getName() + "Release the lock...\n" + ClassLayout.parseInstance(obj).toPrintable());
			}
		}, "thread1").start();

		Thread.sleep(5000);
		log.debug(Thread.currentThread().getName() + "End state...\n" + ClassLayout.parseInstance(obj).toPrintable());
	}

  • results of enforcement

  • It can be seen from the results that the unlocked state becomes biased lock after 4 seconds, and then the state is biased lock all the time!
  • After entering the synchronization code block, the biased thread of the lock changes from 0 to a specific thread.

Call the hashCode() method outside the synchronization code block

  • After entering the synchronization code block, the lock is upgraded to a lightweight lock
  • When the object can be biased (thread ID 0), MarkWord will become unlocked and can only be upgraded to a lightweight lock.

Call the hashCode() method within the synchronization code block

  • Upgrade directly to heavyweight lock
  • When the object is in a bias lock, calling HashCode will force the bias lock to be upgraded to a weight lock.

Partial lock revocation: verify wait and notify yourself

  • Call obj. Of the lock object Hashcode() or system The identityhashcode (obj) method will cause the bias lock of the object to be revoked.
  • Because for an object, its hashcode will only be generated and saved once, and there is no place to save hashcode for partial lock.
  • Lightweight locks record hashCode in the lock record.
  • The heavyweight lock will record the hashCode in the Monitor.
  • When the object can be biased (thread ID 0), MarkWord will become unlocked and can only be upgraded to a lightweight lock.
  • When the object is in a bias lock, calling HashCode will force the bias lock to be upgraded to a weight lock.
  • Execute obj in the bias lock state Notify() will be upgraded to a lightweight lock.
  • Call obj Wait (timeout) will be upgraded to heavyweight lock.

Lightweight Locking

  • If the bias lock fails, the virtual machine will not be upgraded to a heavyweight lock immediately. It will also try to use an optimization method called lightweight lock. At this time, the structure of Mark Word will also become a lightweight lock structure.
  • The scenario of lightweight lock is that threads execute synchronization blocks alternately. If multiple threads access the same lock at the same time, it will lead to the expansion of lightweight lock into heavyweight lock.
  • The lightweight lock directly changes to the unlocked state when it is downgraded! (before viewing, call the hashCode() method outside the synchronization code block)

Simulate the scene where the competition is not fierce

@Slf4j
public class TestMemory {

	public static void main(String[] args) throws InterruptedException {
		log.debug(Thread.currentThread().getName() + "The initial state...\n"
				+ ClassLayout.parseInstance(new Object()).toPrintable());
		// The HotSpot virtual machine will not turn on the bias lock mode for each newly created object until it has a 4s delay after startup
		Thread.sleep(4000);
		Object obj = new Object();

		Thread thread1 = new Thread(new Runnable() {
			@Override
			public void run() {
				log.debug(Thread.currentThread().getName() + "Start execution thread1. . . \n"
						+ ClassLayout.parseInstance(obj).toPrintable());
				synchronized (obj) {
					log.debug(Thread.currentThread().getName() + "Lock acquisition in progress thread1. . . \n"
							+ ClassLayout.parseInstance(obj).toPrintable());
				}
				log.debug(Thread.currentThread().getName() + "Release lock thread1. . . \n"
						+ ClassLayout.parseInstance(obj).toPrintable());
			}
		}, "thread1");
		thread1.start();

		// Control thread contention timing
		Thread.sleep(1);

		Thread thread2 = new Thread(new Runnable() {
			@Override
			public void run() {
				log.debug(Thread.currentThread().getName() + "Start execution thread2. . . \n"
						+ ClassLayout.parseInstance(obj).toPrintable());
				synchronized (obj) {
					log.debug(Thread.currentThread().getName() + "Lock acquisition in progress thread2. . . \n"
							+ ClassLayout.parseInstance(obj).toPrintable());
				}
				log.debug(Thread.currentThread().getName() + "Release lock thread2. . . \n"
						+ ClassLayout.parseInstance(obj).toPrintable());
			}
		}, "thread2");
		thread2.start();

		Thread.sleep(5000);
		log.debug(Thread.currentThread().getName() + "End state...\n" + ClassLayout.parseInstance(obj).toPrintable());
	}
}


Running results of scenarios with less fierce competition

Heavyweight lock

  • The lightweight lock is selected once. If the lock is not obtained, it will directly expand into a heavyweight lock.
  • The heavyweight lock is based on the Monitor mechanism, and the hashCode is recorded in the Monitor

Simulate the scene of fierce competition

  • Remove the following code from the non competitive scenario, which is a highly competitive scenario
// Control thread contention timing
Thread.sleep(1);

Running results of highly competitive scenarios

Concluding remarks

  • Get more valuable articles and let's become architects together!
  • Paying attention to the official account gives you a deep understanding of MySQL.
  • Pay attention to official account and keep continuous understanding of concurrent programming every day!
  • Pay attention to the official account, and follow the continuous and efficient understanding of spring source code.
  • This official account is not advertising!!! Update daily!!!

Keywords: Concurrent Programming

Added by dink87522 on Tue, 01 Feb 2022 22:58:36 +0200