Implementation principle of synchronized

Keywords: Programming Java jvm network

What's the difference between having synchronized or not?

What does synchronized lock as a pessimistic lock?

How to use synchronized code block

In the previous three articles, we talked about two uses of synchronized: synchronization method and synchronization code block, as well as two lock mechanisms: Lock instance object and lock Class object. Today, let's take a look at the implementation principle of synchronization method and synchronization code block.

Let's write all the synchronized methods mentioned in the previous three articles together, as shown below.

public class SynchronizedPrincipleTest {

    public void testNoSynchronized() {
        System.out.println("hello testNoSynchronized");
    }

    public synchronized void testSynchronizedMethod() {
        System.out.println("hello testSynchronizedMethod");
    }

    public static synchronized void testSynchronizedStatic() {
        System.out.println("hello testSynchronizedStatic");
    }

    public void testSynchronizedCodethis() {
        synchronized (this) {
            System.out.println("hello testSynchronizedCode");
        }
    }

    private Object lock = new Object();
    public void testSynchronizedCodeObject() {
        synchronized (lock) {
            System.out.println("hello testSynchronizedCodeObject");
        }
    }

    public void testSynchronizedCodeClass() {
        synchronized (SynchronizedPrincipleTest.class) {
            System.out.println("hello testSynchronizedCodeClass");
        }
    }
}

After writing the code, we compile the code by javac command, and decompile the assembly code by javap command. The command is as follows.

javac SynchronizedPrincipleTest.java
javap -v SynchronizedCodeTest.class

So we need to assemble the code.

Classfile /D:/Workspace/finance/test/thread/src/main/java/com/liebrother/study/synchronizeds/SynchronizedPrincipleTest.class
  Last modified Apr 26, 2020; size 1363 bytes
  MD5 checksum a03ec0b152580bb465b1defe7965a60d
  Compiled from "SynchronizedPrincipleTest.java"
public class com.liebrother.study.synchronizeds.SynchronizedPrincipleTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #2.#31         // java/lang/Object."<init>":()V
   #2 = Class              #32            // java/lang/Object
   #3 = Fieldref           #11.#33        // com/liebrother/study/synchronizeds/SynchronizedPrincipleTest.lock:Ljava/lang/Object;
   #4 = Fieldref           #34.#35        // java/lang/System.out:Ljava/io/PrintStream;
   #5 = String             #36            // hello testNoSynchronized
   #6 = Methodref          #37.#38        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #7 = String             #39            // hello testSynchronizedMethod
   #8 = String             #40            // hello testSynchronizedStatic
   #9 = String             #41            // hello testSynchronizedCode
  #10 = String             #42            // hello testSynchronizedCodeObject
  #11 = Class              #43            // com/liebrother/study/synchronizeds/SynchronizedPrincipleTest
  #12 = String             #44            // hello testSynchronizedCodeClass
  #13 = Utf8               lock
  #14 = Utf8               Ljava/lang/Object;
  #15 = Utf8               <init>
  #16 = Utf8               ()V
  #17 = Utf8               Code
  #18 = Utf8               LineNumberTable
  #19 = Utf8               testNoSynchronized
  #20 = Utf8               testSynchronizedMethod
  #21 = Utf8               testSynchronizedStatic
  #22 = Utf8               testSynchronizedCodethis
  #23 = Utf8               StackMapTable
  #24 = Class              #43            // com/liebrother/study/synchronizeds/SynchronizedPrincipleTest
  #25 = Class              #32            // java/lang/Object
  #26 = Class              #45            // java/lang/Throwable
  #27 = Utf8               testSynchronizedCodeObject
  #28 = Utf8               testSynchronizedCodeClass
  #29 = Utf8               SourceFile
  #30 = Utf8               SynchronizedPrincipleTest.java
  #31 = NameAndType        #15:#16        // "<init>":()V
  #32 = Utf8               java/lang/Object
  #33 = NameAndType        #13:#14        // lock:Ljava/lang/Object;
  #34 = Class              #46            // java/lang/System
  #35 = NameAndType        #47:#48        // out:Ljava/io/PrintStream;
  #36 = Utf8               hello testNoSynchronized
  #37 = Class              #49            // java/io/PrintStream
  #38 = NameAndType        #50:#51        // println:(Ljava/lang/String;)V
  #39 = Utf8               hello testSynchronizedMethod
  #40 = Utf8               hello testSynchronizedStatic
  #41 = Utf8               hello testSynchronizedCode
  #42 = Utf8               hello testSynchronizedCodeObject
  #43 = Utf8               com/liebrother/study/synchronizeds/SynchronizedPrincipleTest
  #44 = Utf8               hello testSynchronizedCodeClass
  #45 = Utf8               java/lang/Throwable
  #46 = Utf8               java/lang/System
  #47 = Utf8               out
  #48 = Utf8               Ljava/io/PrintStream;
  #49 = Utf8               java/io/PrintStream
  #50 = Utf8               println
  #51 = Utf8               (Ljava/lang/String;)V
{
  public com.liebrother.study.synchronizeds.SynchronizedPrincipleTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: new           #2                  // class java/lang/Object
         8: dup
         9: invokespecial #1                  // Method java/lang/Object."<init>":()V
        12: putfield      #3                  // Field lock:Ljava/lang/Object;
        15: return
      LineNumberTable:
        line 7: 0
        line 27: 4

  /** Code without synchronized decoration */
  public void testNoSynchronized();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #5                  // String hello testNoSynchronized
         5: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 10: 0
        line 11: 8

  /** synchronized Decorated instance method */
  public synchronized void testSynchronizedMethod();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED /** Method ID has one more acc х synchronized */
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #7                  // String hello testSynchronizedMethod
         5: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 14: 0
        line 15: 8

  /** synchronized Modified static method */
  public static synchronized void testSynchronizedStatic();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED /** Method ID has more ACC? Static and ACC? Synchronized */
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #8                  // String hello testSynchronizedStatic
         5: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 18: 0
        line 19: 8

  /** synchronized Decorated this code block */
  public void testSynchronizedCodethis();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter     /** Enter the monitor lock through the monitorenter command  */
         4: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         7: ldc           #9                  // String hello testSynchronizedCode
         9: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        12: aload_1
        13: monitorexit      /** Exit the monitor lock through the monitorexit command  */
        14: goto          22
        17: astore_2
        18: aload_1
        19: monitorexit      /** Exit the monitor lock through the monitorexit command  */
        20: aload_2
        21: athrow
        22: return
      Exception table:
         from    to  target type
             4    14    17   any
            17    20    17   any
      LineNumberTable:
        line 22: 0
        line 23: 4
        line 24: 12
        line 25: 22
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 17
          locals = [ class com/liebrother/study/synchronizeds/SynchronizedPrincipleTest, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  /** synchronized Decorated object code block */
  public void testSynchronizedCodeObject();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: getfield      #3                  // Field lock:Ljava/lang/Object;
         4: dup
         5: astore_1
         6: monitorenter      /** Enter the monitor lock through the monitorenter command  */
         7: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        10: ldc           #10                 // String hello testSynchronizedCodeObject
        12: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        15: aload_1
        16: monitorexit       /** Exit the monitor lock through the monitorexit command  */
        17: goto          25
        20: astore_2
        21: aload_1
        22: monitorexit       /** Exit the monitor lock through the monitorexit command  */
        23: aload_2
        24: athrow
        25: return
      Exception table:
         from    to  target type
             7    17    20   any
            20    23    20   any
      LineNumberTable:
        line 29: 0
        line 30: 7
        line 31: 15
        line 32: 25
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 20
          locals = [ class com/liebrother/study/synchronizeds/SynchronizedPrincipleTest, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  /** synchronized Decorated xxx.Class code block */
  public void testSynchronizedCodeClass();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #11                 // class com/liebrother/study/synchronizeds/SynchronizedPrincipleTest
         2: dup
         3: astore_1
         4: monitorenter       /** Enter the monitor lock through the monitorenter command  */
         5: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         8: ldc           #12                 // String hello testSynchronizedCodeClass
        10: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        13: aload_1
        14: monitorexit       /** Exit the monitor lock through the monitorexit command  */
        15: goto          23
        18: astore_2
        19: aload_1
        20: monitorexit       /** Exit the monitor lock through the monitorexit command  */
        21: aload_2
        22: athrow
        23: return
      Exception table:
         from    to  target type
             5    15    18   any
            18    21    18   any
      LineNumberTable:
        line 35: 0
        line 36: 5
        line 37: 13
        line 38: 23
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 18
          locals = [ class com/liebrother/study/synchronizeds/SynchronizedPrincipleTest, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4
}
SourceFile: "SynchronizedPrincipleTest.java"

There are a lot of code in this section, and some comments are added for your convenience. Here I will take some important points to talk about.

  1. We can see that the synchronization method is different from the synchronization implementation of the synchronization code block.

The implementation of the synchronization method is to add the ACC ﹣ synchronized flag to the method flag, which is an implicit implementation. Specifically, when the JVM executes the method, it checks whether there is the ACC ﹣ synchronized synchronization flag. If there is one, it will wait for the monitor monitor to be obtained, and then release the monitor at the end of the method execution.

The implementation of the synchronization code block is to add the monitorenter instruction before the synchronization code block and the monitorexit instruction after the synchronization code block. Each object has a monitor. When the monitor is occupied by a thread, the thread locks the monitor. Each monitor maintains its own counter. When the monitorenter is executed, the counter + 1. When the monitorexit is executed, the lock is released and the counter becomes 0. Only other threads can try to get monitor and operate on shared resources.

  1. The difference between the synchronous instance method testSynchronizedMethod() and the synchronous static method testSynchronizedMethod() is only that whether flags have ACC ﹣ static identity. In fact, whether the lock instance object or the lock Class object is also determined by the JVM bottom implementation according to this identity, which is transparent to us.

  2. What object this VS object VS xxx.class is locked by the synchronization code block, which can be seen in this assembly code.

The code for locking this is as follows. Before entering the monitor listener, obtain this object, that is, the monitor lock of this object.

 0: aload_0        /** Load the current this object */
 1: dup            /** Push this object to the top of the stack */
 2: astore_1       /** Take this object from the top of the stack */
 3: monitorenter   /** Get the monitor lock of this object */

The code of the lock object is as follows. Before entering the monitor listener, obtain the lock object, that is, the monitor lock of the lock object.

0: aload_0          /** Load the current this object */
1: getfield      #3 / * * get the instance variable lock of this object * / / / field lock: ljava / Lang / object;
4: dup              /** Push the instance variable lock to the top of the stack */
5: astore_1         /** Take the lock object from the top of the stack */
6: monitorenter     /** Get monitor lock of lock object */

The code of lock xxx.class is as follows. Before entering the monitor listener, obtain the Class object, that is, the monitor lock of the Class object.

0: ldc           #11 / * * get SynchronizedPrincipleTest class object from constant pool * / / / class COM / liebrother / study / synchronizes / SynchronizedPrincipleTest
2: dup               /** Push the Class object to the top of the stack */
3: astore_1          /** Take the Class object from the top of the stack */
4: monitorenter      /** Get monitor lock of Class object */

Today, from the Java assembly code to analyze the synchronization method and the underlying implementation of the synchronization code block, in fact, this is not really the underlying implementation, but on the Java level, this is the bottom layer. Standing at the top level of the JVM, we will analyze from the perspective of the JVM why the synchronization method can realize multithreading synchronization by adding ACC ﹣ synchronized and synchronization code block plus monitorenter & monitorexit?

Quietly put in a preventive injection. The next article will be a little obscure, but I think it's necessary to understand it, understand the bottom principle, so many threads are not afraid, understand it, and the AQS that I will tell you later is easy to understand. It is to move the implementation of the bottom layer of the JVM to the Java source library.

It's not easy to be original. Thank you very much!

Recommended reading

How to use synchronized code block

What does synchronized lock as a pessimistic lock?

What's the difference between having synchronized or not?

Java daemons from the perspective of JVM

Write Java code for many years, and finally debug to the JVM

The latest and simplest OpenJDK13 code compilation of the whole network

Understand the priority of Java threads and the priority of the corresponding operating system, or you will step on the pit

The most basic knowledge of thread

The boss told you to stop blocking

You can learn Serial, parallel and concurrent by eating fast food

Make a cup of tea and learn from each other

How much do you know about the process?

Do you forget and forget the design pattern?

Reply "design mode" in the background, you can get "one story one design mode" e-book

Feel the article useful to help forward & like, thank you!

Posted by Jonah Bron on Tue, 28 Apr 2020 18:58:00 -0700