一、synchronized加锁对象是什么
为了方便参考,以下是调用加了synchronized方法的代码:
public class Test02_Synchronized {
public static void main(String[] args) {
init();
}
public static void init() {
OutPut outPut = new OutPut();
new Thread(() -> {
//CPU一直转
while (true) {
SleepUtils.millis(5);
outPut.print("atguigu");
}
}).start();
new Thread(() -> {
//CPU一直转
while (true) {
SleepUtils.millis(5);
OutPut.print2("online");
}
}).start();
}
}
1.普通方法(对象方法) synchronized默认加锁对象是this
class OutPut {
public synchronized void print(String name) {
int length = name.length();
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
2.静态方法(类方法) synchronized默认加锁对象是XXX.class
class OutPut {
public static synchronized void print2(String name) {
int length = name.length();
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
3.synchronized同步代码块加锁对象为指定对象
class OutPut {
public void print(String name) {
int length = name.length();
// synchronized (this) {
synchronized (OutPut.class) {
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
synchronized加锁对象是什么的总结:
1.普通方法(对象方法) synchronized默认加锁对象是this
2.静态方法(类方法) synchronized默认加锁对象是XXX.class
3.synchronized同步代码块加锁对象为指定对象
二、synchronized底层原理
synchronized底层原理代码示例
public class Test03_MySynchronized {
public static void main(String[] args) {
// 对Synchronized Class对象进行加锁
synchronized (Test03_MySynchronized.class) {
System.out.println("atguigu");
}
// 静态同步方法,对Synchronized Class对象进行加锁
m();
}
public static synchronized void m() {
System.out.println("online");
}
}
synchronized底层原理代码分析过程
javap
这里我默认你已经安装java并配置好了java的环境变量了。
我们在编译完.java文件后,在target目录下获得.class文件。Test03_MySynchronized.class的文件目录下打开dos窗口,输入javap 反编译class文件。
javap -v Test03_MySynchronized.class
它的执行结果如下:
Classfile /E:/atguigu/2022-12-JavaFinal/day116-JUC/day-JUC01/代码/JUC/JUC/target/classes/com/atguigu/day01/Test03_MySynchronized.class
Last modified 2023-12-13; size 795 bytes
MD5 checksum 7a83f32892224d93b7a81aeec927408d
Compiled from "Test03_MySynchronized.java"
public class com.atguigu.day01.Test03_MySynchronized
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #8.#27 // java/lang/Object."<init>":()V
#2 = Class #28 // com/atguigu/day01/Test03_MySynchronized
#3 = Fieldref #29.#30 // java/lang/System.out:Ljava/io/PrintStream;
#4 = String #31 // atguigu
#5 = Methodref #32.#33 // java/io/PrintStream.println:(Ljava/lang/String;)V
#6 = Methodref #2.#34 // com/atguigu/day01/Test03_MySynchronized.m:()V
#7 = String #35 // online
#8 = Class #36 // java/lang/Object
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 LocalVariableTable
#14 = Utf8 this
#15 = Utf8 Lcom/atguigu/day01/Test03_MySynchronized;
#16 = Utf8 main
#17 = Utf8 ([Ljava/lang/String;)V
#18 = Utf8 args
#19 = Utf8 [Ljava/lang/String;
#20 = Utf8 StackMapTable
#21 = Class #19 // "[Ljava/lang/String;"
#22 = Class #36 // java/lang/Object
#23 = Class #37 // java/lang/Throwable
#24 = Utf8 m
#25 = Utf8 SourceFile
#26 = Utf8 Test03_MySynchronized.java
#27 = NameAndType #9:#10 // "<init>":()V
#28 = Utf8 com/atguigu/day01/Test03_MySynchronized
#29 = Class #38 // java/lang/System
#30 = NameAndType #39:#40 // out:Ljava/io/PrintStream;
#31 = Utf8 atguigu
#32 = Class #41 // java/io/PrintStream
#33 = NameAndType #42:#43 // println:(Ljava/lang/String;)V
#34 = NameAndType #24:#10 // m:()V
#35 = Utf8 online
#36 = Utf8 java/lang/Object
#37 = Utf8 java/lang/Throwable
#38 = Utf8 java/lang/System
#39 = Utf8 out
#40 = Utf8 Ljava/io/PrintStream;
#41 = Utf8 java/io/PrintStream
#42 = Utf8 println
#43 = Utf8 (Ljava/lang/String;)V
{
public com.atguigu.day01.Test03_MySynchronized();
descriptor: ()V
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 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/atguigu/day01/Test03_MySynchronized;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: ldc #2 // class com/atguigu/day01/Test03_MySynchronized
2: dup
3: astore_1
4: monitorenter
5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #4 // String atguigu
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: aload_1
14: monitorexit
15: goto 23
18: astore_2
19: aload_1
20: monitorexit
21: aload_2
22: athrow
23: invokestatic #6 // Method m:()V
26: return
Exception table:
from to target type
5 15 18 any
18 21 18 any
LineNumberTable:
line 6: 0
line 7: 5
line 8: 13
line 10: 23
line 11: 26
LocalVariableTable:
Start Length Slot Name Signature
0 27 0 args [Ljava/lang/String;
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 18
locals = [ class "[Ljava/lang/String;", class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4
public static synchronized void m();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=0, args_size=0
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #7 // String online
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 14: 0
line 15: 8
}
SourceFile: "Test03_MySynchronized.java"
java指令简单说明
ldc
ldc #3,是从常量池加载 #3 数据到操作数栈
javap结果分析
Constant pool:这段可以看出是常量池
我们看main函数后面的代码,如下图
ldc #2,这段表示将常量池中的#2的值推送至栈顶,我们看看常量池中#2的值,如下图
#2 后面写常量池的#28,我们找到#28,可以看到是我们的全限定名
上面不会没关系,我们看一下synchronized的指令码是什么,下面是java代码
继续看main函数的指令码
4: monitorenter
5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #4 // String atguigu
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: aload_1
14: monitorexit
我们可以看到monitorenter和monitorexit
monitorenter表示进入synchronized同步代码块
monitorexit表示退出synchronized同步代码块
这就是加synchronized锁的底层java指令。以下是synchronized底层的执行流程
synchronized底层原理总结:
利用反编译 javap -v xxx.class
它有一个monitorenter和monitorexit的过程
如果一个线程拿到monitor另外一个线程去拿就会失败,失败之后进入一个同步队列(BLOCKED阻塞状态)