1 synchronized keyword
Synchronized is a keyword in Java, which is a kind of synchronization lock (also a pessimistic lock). The objects it modifies are as follows:
- Acting on the instance method , the current instance is locked, and the lock of the current instance must be obtained before entering the synchronization code - the object lock ;
- Act on the code block , lock the object configured in the brackets - object lock ;
- Acting on static methods , the current class is locked, and the lock of the current class object must be obtained before entering the synchronization code—— class lock ;
Note: synchronized cannot modify variables;
2 javap instruction
javap is an anti-parsing tool that comes with jdk. Its function is to de-parse the code area (assembly instruction), local variable table, exception table, code line offset mapping table, constant pool and other information corresponding to the current class according to the class bytecode file.
javap -c
- -c stands for disassembling the code;
- javap -v ***.class file decompilation
- -v -verbose output additional information, such as line number, local variable table, disassembly and other more detailed information;
3 Analyze synchronized implementation from the perspective of bytecode
3.1 Synchronized code blocks
code example
public class LockSyncTest {
// 一个实例对象
Object object = new Object();
public void m1(){
// 同步代码块
synchronized (object) {
System.out.println("这里是同步代码块");
}
}
public static void main(String[] args) {
}
}
decompile
javap -c LockSyncTest.class
generate content
Compiled from "LockSyncTest.java"
public class LockSyncTest {
java.lang.Object object;
public LockSyncTest();
Code:
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 object:Ljava/lang/Object;
15: return
public void m1();
Code:
0: aload_0
1: getfield #3 // Field object:Ljava/lang/Object;
4: dup
5: astore_1
// 获得监视器并进入
6: monitorenter
7: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
// here
10: ldc #5 // String 这里是同步代码块
12: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
15: aload_1
// 锁退出
16: monitorexit
17: goto 25
20: astore_2
21: aload_1
// 底层做了异常处理 如果程序出现异常 底层也要保证释放锁
22: monitorexit
23: aload_2
24: athrow
25: return
Exception table:
from to target type
7 17 20 any
20 23 20 any
public static void main(java.lang.String[]);
Code:
0: return
}
two important instructions
- monitorenter, get the lock;
- monitorexit, release the lock (two, and release the lock when an exception occurs in the underlying implementation);
Must be one enter and two exits?
——Not necessarily, if you throw an exception in the synchronous code block, there is only one enter and one exit;
3.2 Common Synchronization Methods
code example
public class LockSyncTest {
// 普通同步方法
public synchronized void m2(){
System.out.println("普通同步方法");
}
public static void main(String[] args) {
}
}
decompile
javap -v LockSyncTest.class
generate content
The call instruction will check whether the ACC_SYNCHRONIZED access flag of the method is set. If it is set, the execution thread will first hold the monitor and then execute the method, and finally release the monitor when the method is completed (whether it is completed normally or abnormally);
Classfile out/production/ch16/LockSyncTest.class
Last modified 2023-4-11; size 608 bytes
MD5 checksum ff2328e7a59f847a473b7ed814cc108b
Compiled from "LockSyncTest.java"
public class LockSyncTest
SourceFile: "LockSyncTest.java"
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#21 // java/lang/Object."<init>":()V
#2 = Fieldref #22.#23 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #24 // 普通同步方法
#4 = Methodref #25.#26 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #27 // LockSyncTest
#6 = Class #28 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 LLockSyncTest;
#14 = Utf8 m2
#15 = Utf8 main
#16 = Utf8 ([Ljava/lang/String;)V
#17 = Utf8 args
#18 = Utf8 [Ljava/lang/String;
#19 = Utf8 SourceFile
#20 = Utf8 LockSyncTest.java
#21 = NameAndType #7:#8 // "<init>":()V
#22 = Class #29 // java/lang/System
#23 = NameAndType #30:#31 // out:Ljava/io/PrintStream;
#24 = Utf8 普通同步方法
#25 = Class #32 // java/io/PrintStream
#26 = NameAndType #33:#34 // println:(Ljava/lang/String;)V
#27 = Utf8 LockSyncTest
#28 = Utf8 java/lang/Object
#29 = Utf8 java/lang/System
#30 = Utf8 out
#31 = Utf8 Ljava/io/PrintStream;
#32 = Utf8 java/io/PrintStream
#33 = Utf8 println
#34 = Utf8 (Ljava/lang/String;)V
{
public LockSyncTest();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LLockSyncTest;
public synchronized void m2();
// ACC_SYNCHRONIZED标识
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String 普通同步方法
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 6: 0
line 7: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this LLockSyncTest;
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 11: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 args [Ljava/lang/String;
}
3.3 Static synchronization method
code example
public class LockSyncTest {
// 静态同步方法
public static synchronized void m3(){
System.out.println("静态同步方法");
}
public static void main(String[] args) {
}
}
decompile
javap -v LockSyncTest.class
generate content
ACC_STATIC, ACC_SYNCHRONIZED identifiers, to distinguish whether the method is a static synchronization method;
Classfile out/production/ch16/LockSyncTest.class
Last modified 2023-4-11; size 590 bytes
MD5 checksum e551241d2333dd7b754d24e72063d547
Compiled from "LockSyncTest.java"
public class LockSyncTest
SourceFile: "LockSyncTest.java"
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#21 // java/lang/Object."<init>":()V
#2 = Fieldref #22.#23 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #24 // 静态同步方法
#4 = Methodref #25.#26 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #27 // LockSyncTest
#6 = Class #28 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 LLockSyncTest;
#14 = Utf8 m3
#15 = Utf8 main
#16 = Utf8 ([Ljava/lang/String;)V
#17 = Utf8 args
#18 = Utf8 [Ljava/lang/String;
#19 = Utf8 SourceFile
#20 = Utf8 LockSyncTest.java
#21 = NameAndType #7:#8 // "<init>":()V
#22 = Class #29 // java/lang/System
#23 = NameAndType #30:#31 // out:Ljava/io/PrintStream;
#24 = Utf8 静态同步方法
#25 = Class #32 // java/io/PrintStream
#26 = NameAndType #33:#34 // println:(Ljava/lang/String;)V
#27 = Utf8 LockSyncTest
#28 = Utf8 java/lang/Object
#29 = Utf8 java/lang/System
#30 = Utf8 out
#31 = Utf8 Ljava/io/PrintStream;
#32 = Utf8 java/io/PrintStream
#33 = Utf8 println
#34 = Utf8 (Ljava/lang/String;)V
{
public LockSyncTest();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LLockSyncTest;
public static synchronized void m3();
// ACC_STATIC, ACC_SYNCHRONIZED标识符
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=0, args_size=0
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String 静态同步方法
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 6: 0
line 7: 8
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 11: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 args [Ljava/lang/String;
}
4 What is the synchronized lock
Q: Why can any object be a lock?
A: In the HotSpot virtual machine, the monitor is implemented by ObjectMonitor;
4.1 What is a monitor?
Monitors, also called monitors , are a program structure in which multiple worker threads formed by multiple subprograms (objects or modules) within the structure mutually exclusive access to shared resources.
These shared resources are generally hardware devices or a group of variables. All operations that can be performed on shared variables are concentrated in one module. (The semaphore and its operating primitives are "encapsulated" inside an object) The monitor realizes that at one point in time, at most one thread is executing a certain subroutine of the monitor . The monitor provides a mechanism. The monitor can be regarded as a software module. It encapsulates the shared variables and the operations on these shared variables to form a functional module with a certain interface. The process can call the monitor to realize the process level . concurrency control .
4.2 Interpretation of the underlying C++ code
ObjectMonitor.java -> ObjectMonitor.cpp ->ObjectMonitor.hpp
Part of the source code of ObjectMonitor.hpp:
ObjectMonitor() {
_header = NULL;
_count = 0;
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL;
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ;
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
_previous_owner_tid = 0;
}
Several key attributes in ObjectMonitor: