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.
- 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.
-
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.
-
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
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!