Chapter 2 go deep into synchronized keyword

1. introduction

Thread safety is the top priority of concurrent programming, and there are two main causes of concurrent problems.
1. Critical resources exist
2. Multiple threads operate on shared data at the same time
Based on this situation, we will consider a mechanism that when one thread accesses critical resources, other threads cannot access them. This mechanism is called mutex. Synchronized in java is such a mechanism that only one thread can access the decorated code block at the same time, thus ensuring the security. Synchronized also ensures that the changes of one thread (mainly the changes of shared data) are seen by other threads, that is to say, variables in the synchronized code block do not need to use the volatile keyword.

2. Three ways to use synchronized

Modify the instance method to lock the current instance and obtain the lock of the current instance before entering the synchronization code
Modify the static method to lock the current class object, and obtain the lock of the current class object before entering the synchronization code
Decorate the code block, specify the lock object, lock the given object, and obtain the lock of the given object before entering the synchronization code base.

package com.hqa.design.test;

import java.util.concurrent.CountDownLatch;

public class Test {

	/**
	 * An instance lock
	 */
	private static final Object lock = new Object();

	/**
	 * Static method lock, acting on Test.class object
	 */
	public static synchronized void classLock() {
		try {
			System.out.println(Thread.currentThread().getName() + "Get into classLock Method,Dormancy 5 s");
			Thread.sleep(5000);
			System.out.println(Thread.currentThread().getName() + "Sign out classLock Method");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Object instance lock, acting on the current Test class instance
	 */
	public synchronized  void thisLock() {
			try {
				System.out.println(Thread.currentThread().getName() + "Get into thisLock Method,Dormancy 5 s");
				Thread.sleep(5000);
				System.out.println(Thread.currentThread().getName() + "Sign out thisLock Method");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}		
	}

	/**
	 * Lock object is lock
	 */
	public void objLock() {
		synchronized (lock) {
			try {
				System.out.println(Thread.currentThread().getName() + "Get into objLock Method,Dormancy 5 s");
				Thread.sleep(5000);
				System.out.println(Thread.currentThread().getName() + "Sign out objLock Method");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

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

		//Declare a lock. The function of this class will not be discussed now. You only need to know this class here
		//It is to let the main thread continue to execute after all the sub threads have finished executing
		final CountDownLatch cnt = new CountDownLatch(2);

		// classLock test
		Thread t1 = new Thread(() -> {
			Test.classLock();
			cnt.countDown();
		});

		Thread t2 = new Thread(() -> {
			Test.classLock();
			cnt.countDown();
		});

		t1.start();
		t2.start();

		cnt.await();
		System.out.println("===============classLock Test Over==============");

		CountDownLatch cnt2 = new CountDownLatch(2);

		// thisLock test
		Test t = new Test();

		Thread t3 = new Thread(() -> {
			t.thisLock();
			cnt2.countDown();
		});

		Thread t4 = new Thread(() -> {
			t.thisLock();
			cnt2.countDown();
		});

		t3.start();
		t4.start();
		cnt2.await();
		System.out.println("===============thisLock Test Over==============");

		CountDownLatch cnt3 = new CountDownLatch(2);
		
		// Obklock test
		Thread t5 = new Thread(() -> {
			t.objLock();
			cnt3.countDown();
		});

		Thread t6 = new Thread(() -> {
			t.objLock();
			cnt3.countDown();
		});

		t5.start();
		t6.start();

		cnt3.await();
		System.out.println("===============objLock Test Over==============");
	}

}

Output:

Thread-0 enters the classLock method and sleeps for 5s
 Thread-0 exits classLock method
 Thread-1 enters the classLock method and sleeps for 5s
 Thread-1 exits classLock method
===============classLock Test Over==============
Thread-3 enters the thisLock method, sleeping for 5s
 Thread-3 exits the thisLock method
 Thread-2 enters thisLock method, and sleeps for 5s
 Thread-2 exits the thisLock method
===============thisLock Test Over==============
Thread-4 enters obklock method and sleeps for 5s
 Thread-4 exits objLock method
 Thread-5 enters obklock method and sleeps for 5s
 Thread-5 exits objLock method
===============objLock Test Over==============

3.synchronized semantics

The synchronization in the JVM virtual machine is based on the Monitor object, whether it is shown or implied here. We need to note that the synchronization method is not implemented by monitorenter and monitorexit, but by the instruction reading the ACC ﹣ synchronized flag of the method in the runtime constant pool.

4.Java object header

In the JVM, the layout of objects in memory is divided into three parts: object header, instance data, and alignment fill. As follows:
ps: picture from: https://blog.csdn.net/javazejian/article/details/72828483

There are two types of object headers.

4.1 common object head

1,Mark Word
2. Pointer to class

4.2 array object header

1,Mark Word
2. Pointer to class
3. Array length (only for array objects)

4.3 Mark Word related to synchronization lock

Mark Word records information about an object and a lock. When the object is treated as a synchronized lock by the synchronized keyword, a series of operations around the lock are related to Mark Word.
Mark Word is 32bit in 32-bit JVM and 64bit in 64 bit JVM.
Mark Word stores different contents in different lock states. This is how it is stored in a 32-bit JVM:

Lock state

25bit

4bit

1bit

2bit

23bit

2bit

Is it biased to lock

Lock flag bit

No lock

Object's HashCode

Age of generational age

0

01

Bias lock

Thread ID

Epoch

Age of generational age

1

01

Lightweight Locking

Pointer to the lock record in the stack

00

Heavyweight lock

Pointer to heavyweight lock

10

GC marker

empty

11

The lock flag bits of unlocked and biased lock are both 01, but the first bit distinguishes whether this is unlocked or biased lock.

After JDK 1.6, there is a concept of lock upgrade when dealing with synchronous locks. The JVM starts to deal with synchronous locks from biased locks. With the increasingly fierce competition, the processing mode is upgraded from biased locks to lightweight locks, and finally to heavyweight locks.

In general, a JVM uses locks and Mark Word as follows:

1. When it is not treated as a lock, this is a common object. Mark Word records the HashCode of the object. The lock flag bit is 01, and whether it is biased to the lock bit is 0.

2. When an object is treated as A synchronous lock and A thread A grabs the lock, the lock flag bit is still 01, but whether it is biased to the lock bit is changed to 1. The first 23 bits record the thread id of the seized lock, indicating that it enters the biased lock state.

3. When thread A tries to acquire the lock again, the JVM finds that the flag bit of the synchronization lock object is 01, and whether the bias lock is 1, that is, the bias state. The thread id recorded in Mark Word is thread A's own id, indicating that thread A has acquired the bias lock and can execute the code of the synchronization lock.

4. When thread B attempts to acquire the lock, the JVM finds that the synchronization lock is in A biased state, but the thread id in Mark Word does not record B, then thread B will first attempt to acquire the lock with CAS operation, which is likely to succeed, because thread A generally does not automatically release the biased lock. If the lock grabbing is successful, change the thread id in Mark Word to the id of thread B, which means that thread B has obtained the biased lock and can execute the synchronous lock code. If the lock grabbing fails, continue with step 5.

5. Failure to grab lock in biased lock state indicates that there is a certain competition between current locks, and biased locks will be upgraded to lightweight locks. The JVM will open a separate space in the thread stack of the current thread, in which the pointer to the object lock Mark Word is saved, and the pointer to this space is saved in the object lock Mark Word. The above two save operations are CAS operations. If the save is successful, it means that the thread has seized the synchronization lock. Change the lock flag bit in Mark Word to 00 to execute the synchronization lock code. If the saving fails, it means that the lock grabbing fails. The competition is too fierce. Continue to step 6.

6. If the lightweight lock fails to grab the lock, the JVM will use the spin lock. The spin lock is not a lock state, but represents a continuous retry, trying to grab the lock. Starting from JDK 1.7, spin lock is enabled by default, and the number of spins is determined by the JVM. If the lock grabbing is successful, execute the synchronization lock code. If it fails, continue to step 7.

7. If the lock still fails after the spin lock retries, the synchronization lock will be upgraded to the heavyweight lock, and the lock flag bit will be changed to 10. In this state, threads that do not grab the lock will be blocked.

5. Heavyweight lock

We mainly analyze the heavyweight lock, that is, the synchronized object lock. The lock identification bit is 10, where the pointer points to the starting address of the monitor object (also known as the pipe or monitor lock). Each object has a monitor associated with it. There are many ways to realize the relationship between the object and its monitor. For example, monitor can be created and destroyed together with the object or generated automatically when the thread tries to acquire the object lock, but when a monitor is held by a thread, it is in the locked state. In the Java virtual machine (HotSpot), monitor is implemented by ObjectMonitor. Its main data structure is as follows (located in the source code ObjectMonitor.hpp file of HotSpot virtual machine, implemented in C + +)

ObjectMonitor() {
    _header       = NULL;
    _count        = 0; //Number of records
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;
    _WaitSet      = NULL; //Threads in the wait state will be added to the WaitSet
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ; //Threads waiting for lock block will be added to the list
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
  }

There are two queues in ObjectMonitor, i.e. "WaitSet" and "EntryList", which are used to save ObjectWaiter object list (each thread waiting for lock will be encapsulated as ObjectWaiter object), "owner" refers to the thread holding ObjectMonitor object. When multiple threads access a synchronous code at the same time, they will first enter the "EntryList" collection, and enter "owner" when the thread obtains the monitor of the object Area and set the owner variable in the monitor to the current thread and add 1 to the counter count in the monitor. If the thread calls the wait() method, the currently held monitor will be released. The owner variable will return to null, and the count will decrease by 1. At the same time, the thread will enter the WaitSet collection and wait to be awakened. If the current thread finishes executing, the monitor will also be released and the value of the variable will be reset so that other threads can get the monitor. As shown in the figure below

The monitor Object exists in the Object header of each Java Object (the point of stored pointer). The synchronized lock obtains the lock in this way, which is also the reason why any Object in Java can be used as a lock, and the reason why notify/notifyAll/wait and other methods exist in the top Object

6.synchronized Decompilation

6.1 prepare a class first

package com.hqa.design.test;

import java.util.concurrent.CountDownLatch;

public class Test {

	/**
	 * An instance lock
	 */
	private static final Object lock = new Object();

	/**
	 * Static method lock, acting on Test.class object
	 */
	public static synchronized void classLock() {
		try {
			System.out.println(Thread.currentThread().getName() + "Get into classLock Method,Dormancy 5 s");
			Thread.sleep(5000);
			System.out.println(Thread.currentThread().getName() + "Sign out classLock Method");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Object instance lock, acting on the current Test class instance
	 */
	public void thisLock() {
		synchronized (this) {
			try {
				System.out.println(Thread.currentThread().getName() + "Get into thisLock Method,Dormancy 5 s");
				Thread.sleep(5000);
				System.out.println(Thread.currentThread().getName() + "Sign out thisLock Method");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * Lock object is lock
	 */
	public void objLock() {
		synchronized (lock) {
			try {
				System.out.println(Thread.currentThread().getName() + "Get into objLock Method,Dormancy 5 s");
				Thread.sleep(5000);
				System.out.println(Thread.currentThread().getName() + "Sign out objLock Method");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}

6.2 view the javap of Test.class

javap -c -l -v Test > out.txt

Output:

Classfile /D:/mcWork/workspace/design-share/bin/com/hqa/design/test/Test.class
  Last modified 2020-1-20; size 1837 bytes
  MD5 checksum eaeadfde841431d8d72d966bc14159ba
  Compiled from "Test.java"
public class com.hqa.design.test.Test
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2             // com/hqa/design/test/Test
   #2 = Utf8               com/hqa/design/test/Test
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               lock
   #6 = Utf8               Ljava/lang/Object;
   #7 = Utf8               <clinit>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Methodref          #3.#11         // java/lang/Object."<init>":()V
  #11 = NameAndType        #12:#8         // "<init>":()V
  #12 = Utf8               <init>
  #13 = Fieldref           #1.#14         // com/hqa/design/test/Test.lock:Ljava/lang/Object;
  #14 = NameAndType        #5:#6          // lock:Ljava/lang/Object;
  #15 = Utf8               LineNumberTable
  #16 = Utf8               LocalVariableTable
  #17 = Utf8               this
  #18 = Utf8               Lcom/hqa/design/test/Test;
  #19 = Utf8               classLock
  #20 = Fieldref           #21.#23        // java/lang/System.out:Ljava/io/PrintStream;
  #21 = Class              #22            // java/lang/System
  #22 = Utf8               java/lang/System
  #23 = NameAndType        #24:#25        // out:Ljava/io/PrintStream;
  #24 = Utf8               out
  #25 = Utf8               Ljava/io/PrintStream;
  #26 = Class              #27            // java/lang/StringBuilder
  #27 = Utf8               java/lang/StringBuilder
  #28 = Methodref          #29.#31        // java/lang/Thread.currentThread:()Ljava/lang/Thread;
  #29 = Class              #30            // java/lang/Thread
  #30 = Utf8               java/lang/Thread
  #31 = NameAndType        #32:#33        // currentThread:()Ljava/lang/Thread;
  #32 = Utf8               currentThread
  #33 = Utf8               ()Ljava/lang/Thread;
  #34 = Methodref          #29.#35        // java/lang/Thread.getName:()Ljava/lang/String;
  #35 = NameAndType        #36:#37        // getName:()Ljava/lang/String;
  #36 = Utf8               getName
  #37 = Utf8               ()Ljava/lang/String;
  #38 = Methodref          #39.#41        // java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
  #39 = Class              #40            // java/lang/String
  #40 = Utf8               java/lang/String
  #41 = NameAndType        #42:#43        // valueOf:(Ljava/lang/Object;)Ljava/lang/String;
  #42 = Utf8               valueOf
  #43 = Utf8               (Ljava/lang/Object;)Ljava/lang/String;
  #44 = Methodref          #26.#45        // java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
  #45 = NameAndType        #12:#46        // "<init>":(Ljava/lang/String;)V
  #46 = Utf8               (Ljava/lang/String;)V
  #47 = String             #48 / / enter classLock method and sleep for 5s
  #48 = Utf8 enters classLock method, sleep for 5s
  #49 = Methodref          #26.#50        // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #50 = NameAndType        #51:#52        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #51 = Utf8               append
  #52 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #53 = Methodref          #26.#54        // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #54 = NameAndType        #55:#37        // toString:()Ljava/lang/String;
  #55 = Utf8               toString
  #56 = Methodref          #57.#59        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #57 = Class              #58            // java/io/PrintStream
  #58 = Utf8               java/io/PrintStream
  #59 = NameAndType        #60:#46        // println:(Ljava/lang/String;)V
  #60 = Utf8               println
  #61 = Long               5000l
  #63 = Methodref          #29.#64        // java/lang/Thread.sleep:(J)V
  #64 = NameAndType        #65:#66        // sleep:(J)V
  #65 = Utf8               sleep
  #66 = Utf8               (J)V
  #67 = String             #68 / / exit classLock method
  #68 = Utf8 exits classLock method
  #69 = Methodref          #70.#72        // java/lang/InterruptedException.printStackTrace:()V
  #70 = Class              #71            // java/lang/InterruptedException
  #71 = Utf8               java/lang/InterruptedException
  #72 = NameAndType        #73:#8         // printStackTrace:()V
  #73 = Utf8               printStackTrace
  #74 = Utf8               e
  #75 = Utf8               Ljava/lang/InterruptedException;
  #76 = Utf8               StackMapTable
  #77 = Utf8               thisLock
  #78 = String             #79 / / enter thisLock method and sleep for 5s
  #79 = Utf8 enters thisLock method, sleep for 5s
  #80 = String             #81 / / exit thisLock method
  #81 = Utf8 exits thisLock method
  #82 = Utf8               objLock
  #83 = String             #84 / / enter obklock method and sleep for 5s
  #84 = Utf8 enters obklock method and sleeps for 5s
  #85 = String             #86 / / exit objLock method
  #86 = Utf8 exits obklock method
  #87 = Class              #88            // java/lang/Throwable
  #88 = Utf8               java/lang/Throwable
  #89 = Utf8               SourceFile
  #90 = Utf8               Test.java
{
  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: new           #3                  // class java/lang/Object
         3: dup
         4: invokespecial #10                 // Method java/lang/Object."<init>":()V
         7: putstatic     #13                 // Field lock:Ljava/lang/Object;
        10: return
      LineNumberTable:
        line 10: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature

  public com.hqa.design.test.Test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #10                 // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/hqa/design/test/Test;

  public static synchronized void classLock();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
    Code:
      stack=4, locals=1, args_size=0
         0: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #26                 // class java/lang/StringBuilder
         6: dup
         7: invokestatic  #28                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
        10: invokevirtual #34                 // Method java/lang/Thread.getName:()Ljava/lang/String;
        13: invokestatic  #38                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
        16: invokespecial #44                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
        19: ldc           #47 / / String enter classLock method, sleep for 5s
        21: invokevirtual #49                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        24: invokevirtual #53                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        27: invokevirtual #56                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        30: ldc2_w        #61                 // long 5000l
        33: invokestatic  #63                 // Method java/lang/Thread.sleep:(J)V
        36: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
        39: new           #26                 // class java/lang/StringBuilder
        42: dup
        43: invokestatic  #28                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
        46: invokevirtual #34                 // Method java/lang/Thread.getName:()Ljava/lang/String;
        49: invokestatic  #38                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
        52: invokespecial #44                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
        55: ldc           #67 / / String exit classLock method
        57: invokevirtual #49                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        60: invokevirtual #53                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        63: invokevirtual #56                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        66: goto          74
        69: astore_0
        70: aload_0
        71: invokevirtual #69                 // Method java/lang/InterruptedException.printStackTrace:()V
        74: return
      Exception table:
         from    to  target type
             0    66    69   Class java/lang/InterruptedException
      LineNumberTable:
        line 17: 0
        line 18: 30
        line 19: 36
        line 20: 66
        line 21: 70
        line 23: 74
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           70       4     0     e   Ljava/lang/InterruptedException;
      StackMapTable: number_of_entries = 2
        frame_type = 247 /* same_locals_1_stack_item_frame_extended */
          offset_delta = 69
          stack = [ class java/lang/InterruptedException ]
        frame_type = 4 /* same */

  public synchronized void thisLock();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=4, locals=2, args_size=1
         0: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #26                 // class java/lang/StringBuilder
         6: dup
         7: invokestatic  #28                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
        10: invokevirtual #34                 // Method java/lang/Thread.getName:()Ljava/lang/String;
        13: invokestatic  #38                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
        16: invokespecial #44                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
        19: ldc           #78 / / String enter thisLock method, sleep for 5s
        21: invokevirtual #49                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        24: invokevirtual #53                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        27: invokevirtual #56                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        30: ldc2_w        #61                 // long 5000l
        33: invokestatic  #63                 // Method java/lang/Thread.sleep:(J)V
        36: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
        39: new           #26                 // class java/lang/StringBuilder
        42: dup
        43: invokestatic  #28                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
        46: invokevirtual #34                 // Method java/lang/Thread.getName:()Ljava/lang/String;
        49: invokestatic  #38                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
        52: invokespecial #44                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
        55: ldc           #80 / / String exit thisLock method
        57: invokevirtual #49                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        60: invokevirtual #53                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        63: invokevirtual #56                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        66: goto          74
        69: astore_1
        70: aload_1
        71: invokevirtual #69                 // Method java/lang/InterruptedException.printStackTrace:()V
        74: return
      Exception table:
         from    to  target type
             0    66    69   Class java/lang/InterruptedException
      LineNumberTable:
        line 30: 0
        line 31: 30
        line 32: 36
        line 33: 66
        line 34: 70
        line 36: 74
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      75     0  this   Lcom/hqa/design/test/Test;
           70       4     1     e   Ljava/lang/InterruptedException;
      StackMapTable: number_of_entries = 2
        frame_type = 247 /* same_locals_1_stack_item_frame_extended */
          offset_delta = 69
          stack = [ class java/lang/InterruptedException ]
        frame_type = 4 /* same */

  public void objLock();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=4, locals=3, args_size=1
         0: getstatic     #13                 // Field lock:Ljava/lang/Object;
         3: dup
         4: astore_1
         5: monitorenter
         6: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
         9: new           #26                 // class java/lang/StringBuilder
        12: dup
        13: invokestatic  #28                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
        16: invokevirtual #34                 // Method java/lang/Thread.getName:()Ljava/lang/String;
        19: invokestatic  #38                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
        22: invokespecial #44                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
        25: ldc           #83 / / String enter obklock method, sleep for 5s
        27: invokevirtual #49                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        30: invokevirtual #53                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        33: invokevirtual #56                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        36: ldc2_w        #61                 // long 5000l
        39: invokestatic  #63                 // Method java/lang/Thread.sleep:(J)V
        42: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
        45: new           #26                 // class java/lang/StringBuilder
        48: dup
        49: invokestatic  #28                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
        52: invokevirtual #34                 // Method java/lang/Thread.getName:()Ljava/lang/String;
        55: invokestatic  #38                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
        58: invokespecial #44                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
        61: ldc           #85 / / String exit objLock method
        63: invokevirtual #49                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        66: invokevirtual #53                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        69: invokevirtual #56                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        72: goto          80
        75: astore_2
        76: aload_2
        77: invokevirtual #69                 // Method java/lang/InterruptedException.printStackTrace:()V
        80: aload_1
        81: monitorexit
        82: goto          88
        85: aload_1
        86: monitorexit
        87: athrow
        88: return
      Exception table:
         from    to  target type
             6    72    75   Class java/lang/InterruptedException
             6    82    85   any
            85    87    85   any
      LineNumberTable:
        line 42: 0
        line 44: 6
        line 45: 36
        line 46: 42
        line 47: 72
        line 48: 76
        line 42: 80
        line 51: 88
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      89     0  this   Lcom/hqa/design/test/Test;
           76       4     2     e   Ljava/lang/InterruptedException;
      StackMapTable: number_of_entries = 4
        frame_type = 255 /* full_frame */
          offset_delta = 75
          locals = [ class com/hqa/design/test/Test, class java/lang/Object ]
          stack = [ class java/lang/InterruptedException ]
        frame_type = 4 /* same */
        frame_type = 68 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 2
}
SourceFile: "Test.java"

Referring to the above documents, we can see the key parts:

Modification method:

 public static synchronized void classLock();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC, `ACC_SYNCHRONIZED`

Decorated object code block:

 public void objLock();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=4, locals=3, args_size=1
         0: getstatic     #13                 // Field lock:Ljava/lang/Object;
         3: dup
         4: astore_1
         5: `monitorenter`
         6: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
         9: new           #26                 // class java/lang/StringBuilder
        .......
        80: aload_1
        81: `monitorexit`
        82: goto          88
        85: aload_1
        86: `monitorexit`

It can be seen from the bytecode that the implementation of the synchronization statement block uses the monitorenter and monitorexit instructions, in which the monitorenter instruction points to the start position of the synchronization code block, and the monitorexit instruction indicates the end position of the synchronization code block. When the monitorenter instruction is executed, the current thread will try to obtain the holding right of the corresponding monitor of the objectref (i.e. the object lock). When the objectr If the entry counter of EF's monitor is 0, the thread can successfully obtain the monitor, and set the counter value to 1, and the lock is successful. If the current thread already has the ownership of the monitor of objectref, it can re-enter the monitor (about re-entry will be analyzed later), and the value of the counter will be increased by 1. If other threads already have the ownership of the monitor of objectref, the current thread will be blocked until the executing thread finishes executing, that is, the monitorexit instruction is executed, the executing thread will release the monitor (lock) and set the counter value to 0, and other threads will have the chance to hold the monitor. It is worth noting that the compiler will ensure that every monitorenter instruction that is invoked in the method will execute its corresponding monitorexit instructions regardless of the way the method is completed, regardless of whether the method ends normally or ends. In order to ensure that the monitorcenter and monitorexit instructions can still be executed correctly when the method exception is completed, the compiler will automatically generate an exception handler, which declares that all exceptions can be handled, and its purpose is to execute the monitorexit instructions. It can also be seen from the bytecode that there is an additional monitorexit instruction, which is the instruction to release the monitor executed at the end of the exception.

6.3 summary

There is no monitorenter instruction and monitorexit instruction for the synchronized decorated method. Instead of the monitorenter instruction and monitorexit instruction, it is the ACC  synchronized identity, which indicates that the method is a synchronous method. The JVM uses the ACC  synchronized access flag to identify whether a method is declared as a synchronous method, so as to execute the corresponding synchronous call. Monitorexit occurs twice because even the party If the lock is abnormal, the lock should be released normally.

7. About lock elimination

Lock elimination refers to the removal of locks that cannot compete for shared resources by running fewer scans even when the JVM is compiled.
Through lock elimination, meaningless lock requests can be saved

For example, when using StringBuffer in a single thread, synchronization is unnecessary. At this time, the JVM can eliminate unnecessary locks based on escape analysis count at runtime.

8. Reentry of synchronized

In terms of the design of mutex locks, when a thread tries to operate the critical resources of an object lock held by other threads, it will be blocked. However, when a thread requests again to hold the critical resources of an object lock, this situation belongs to reentry lock, and the request will succeed. Synchronized in java is based on the atomic internal locking mechanism, which is reentrant, Therefore, when a thread calls the synchronized method, it is allowed to call another synchronized method of the object in the body of the method, that is to say, a thread gets an object lock and requests the object lock again. This is the reentrant nature of synchronized.

9. About wait and notify

Look at a piece of code:

package com.hqa.design.test;

public class Test {

	/**
	 * An instance lock
	 */
	private final Object lock = new Object();
	
	public Object getLock(){
		return lock;
	} 

	/**
	 * Lock object is lock
	 */
	public void lock1() {
		synchronized (lock) {
			try {
				System.out.println(Thread.currentThread().getName() + "Get into lock1 Method,Dormancy 3 s");
				Thread.sleep(3000);
				System.out.println(Thread.currentThread().getName() + ",3s Then release the lock");
				lock.wait();
				lock.notifyAll();
				Thread.sleep(7000);
				System.out.println(Thread.currentThread().getName() + "Sign out lock Method");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * Lock object is lock
	 */
	public void lock2() {
		synchronized (lock) {
			try {
				System.out.println(Thread.currentThread().getName() + "Get into lock2 Method,Dormancy 10 s");
				Thread.sleep(10000);
				lock.notifyAll();
				System.out.println(Thread.currentThread().getName() + ",Release lock,Sign out lock2 Method");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		Test test = new Test();
		
		Thread t1 = new Thread(() -> {
			test.lock1();
		});
		t1.start();
		
		Thread t2 = new Thread(() -> {
			test.lock2();
		});
		t2.start();
	}

}

Output:

Thread-0 enters lock1 method and sleeps for 3s
 Release lock after Thread-0,3s
 Thread-1 enters lock2 method, sleep for 10s
 Thread-1, release lock, exit lock2 method
 Thread-0 exits lock method

The lock object is contested by t1 and t2. In this case, t1 thread await during execution. This operation will release the lock lock, and then inform all other threads to seize the lock again. At this time, t2 will seize the lock, continue execution, and then inform other threads to execute again. We will talk about thread communication in a separate module with limited space, so that's the end of this chapter.

Reference link: https://blog.csdn.net/javazejian/article/details/72828483

Published 25 original articles, won praise 8, visited 4468
Private letter follow

Keywords: Java jvm JDK Programming

Added by DWilliams on Mon, 20 Jan 2020 11:47:54 +0200