开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情
1.Sync初步解读
synchronize作用范围在三个地方是:普通方法;静态方法;同步代码块。
1.普通方法- 锁对象:我们的对象(new 出来的,谁调用这个方法,锁作用于谁身上)
2.静态方法- 锁对象:我们的对象所属的class,全局只有一个。(类型,放到方法区,包括我们的真正的.class文件的二进制文件都最终加载到了运行时数据区的方法区
3.静态代码块- 锁对象:就是我们synchronized关键字后括号里的内容
下面列举一个例子进行深入说明:
public class SyncUsingWay {
// 普通方法- 锁对象:我们的对象(new 出来的,谁调用这个方法,锁作用于谁身上)
public synchronized void SyncMethod() {
System.out.println("SyncMethod");
}
// 静态方法- 锁对象:我们的对象所属的class,全局只有一个。(类型,放到方法区的
// 包括我们的真正的.class文件的二进制文件都最终加载到了运行时数据区的方法区)
public synchronized static void StaticSyncMethod(){
System.out.println("StaticSyncMethod");
}
public void method(){
// 静态代码块
synchronized (this) {
System.out.println("method");
}
}
}
2.Sync深入解读
通过 javap -v 深入查看:
把所有打印摘出如下:
Classfile /G:/test/algorithm/target/classes/com/readBook/juc/SyncUsingWay.class
Last modified 2022-11-25; size 777 bytes
MD5 checksum 5da1ce76ceb1bf7b7ab043c9bac0ea82
Compiled from "SyncUsingWay.java"
public class com.readBook.juc.SyncUsingWay
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #8.#25 // java/lang/Object."<init>":()V
#2 = Fieldref #26.#27 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #16 // SyncMethod
#4 = Methodref #28.#29 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = String #17 // StaticSyncMethod
#6 = String #18 // method
#7 = Class #30 // com/readBook/juc/SyncUsingWay
#8 = Class #31 // java/lang/Object
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 LocalVariableTable
#14 = Utf8 this
#15 = Utf8 Lcom/readBook/juc/SyncUsingWay;
#16 = Utf8 SyncMethod
#17 = Utf8 StaticSyncMethod
#18 = Utf8 method
#19 = Utf8 StackMapTable
#20 = Class #30 // com/readBook/juc/SyncUsingWay
#21 = Class #31 // java/lang/Object
#22 = Class #32 // java/lang/Throwable
#23 = Utf8 SourceFile
#24 = Utf8 SyncUsingWay.java
#25 = NameAndType #9:#10 // "<init>":()V
#26 = Class #33 // java/lang/System
#27 = NameAndType #34:#35 // out:Ljava/io/PrintStream;
#28 = Class #36 // java/io/PrintStream
#29 = NameAndType #37:#38 // println:(Ljava/lang/String;)V
#30 = Utf8 com/readBook/juc/SyncUsingWay
#31 = Utf8 java/lang/Object
#32 = Utf8 java/lang/Throwable
#33 = Utf8 java/lang/System
#34 = Utf8 out
#35 = Utf8 Ljava/io/PrintStream;
#36 = Utf8 java/io/PrintStream
#37 = Utf8 println
#38 = Utf8 (Ljava/lang/String;)V
{
public com.readBook.juc.SyncUsingWay();
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/readBook/juc/SyncUsingWay;
public synchronized void SyncMethod();
descriptor: ()V
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 SyncMethod
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 Lcom/readBook/juc/SyncUsingWay;
public static synchronized void StaticSyncMethod();
descriptor: ()V
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 #5 // String StaticSyncMethod
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 11: 0
line 12: 8
public void method();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #6 // String method
9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_1
13: monitorexit
14: goto 22
17: astore_2
18: aload_1
19: monitorexit
20: aload_2
21: athrow
22: return
Exception table:
from to target type
4 14 17 any
17 20 17 any
LineNumberTable:
line 16: 0
line 17: 4
line 18: 12
line 19: 22
LocalVariableTable:
Start Length Slot Name Signature
0 23 0 this Lcom/readBook/juc/SyncUsingWay;
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 17
locals = [ class com/readBook/juc/SyncUsingWay, class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4
}
SourceFile: "SyncUsingWay.java"
这里我们摘出来三个方法的反编译来分析:
public synchronized void SyncMethod() {
System.out.println("SyncMethod");
}
这个方法反编译对应着下面:
public synchronized void SyncMethod();
descriptor: ()V
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 SyncMethod
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 Lcom/readBook/juc/SyncUsingWay;
我们可以看大flags: ACC_PUBLIC, ACC_SYNCHRONIZED,这个里面的ACC_SYNCHRONIZED就是加锁的方式,所以在普通方法加关键字synchronize的时候,会在flags里加入ACC_SYNCHRONIZED进行标志。
public synchronized static void StaticSyncMethod(){
System.out.println("StaticSyncMethod");
}
这个方法反编译对应着下面:
public static synchronized void StaticSyncMethod();
descriptor: ()V
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 #5 // String StaticSyncMethod
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 11: 0
line 12: 8
同理我们可以看到这个静态方法加关键字synchronize的时候,也会在flags里加入ACC_SYNCHRONIZED进行标志。这里还会比上面多一个ACC_STATIC,因为这个是静态方法的标志。
public void method(){
// 静态代码块
synchronized (this) {
System.out.println("method");
}
}
这个方法反编译对应着下面:
public void method();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: aload_0
1: dup
2: astore_1
3: monitorenter // 进入同步代码块(进入临界范围内,锁的原子内部)
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #6 // String method
9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_1
13: monitorexit // 正常退出同步代码块
14: goto 22
17: astore_2
18: aload_1
19: monitorexit //防止任何异常情况下,退出同步代码块。JVM仍然可以释放锁
20: aload_2
line 19: 22
LocalVariableTable:
Start Length Slot Name Signature
0 23 0 this Lcom/readBook/juc/SyncUsingWay;
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 17
locals = [ class com/readBook/juc/SyncUsingWay, class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4
我们可以看到这个是静态代码块的方式加锁,但是里面flags里并没有ACC_SYNCHRONIZED,那这个是怎么回事呢?
我们可以看到:
3: monitorenter表示进入同步代码块()
13: monitorexit表示正常退出同步代码块
19: monitorexit表示防止任何异常情况下,退出同步代码块。JVM仍然可以释放锁
所以同步代码块和在方法中的不同。