dcLunatic's blog

synchronized关键字

字数统计: 755阅读时长: 3 min
2018/09/21 Share

synchronized关键字

写一个测试类,里面包含了同步方法,同步代码块,还有一个不加同步的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SyncDemo{
public synchronized void doSth1(){
System.out.println("Hello world!");

}
public void doSth2(){
synchronized(SyncDemo.class){
System.out.println("Hello world!");
}
}
public void doSth3(){
System.out.println("Hello world!");
}
}

javac编译后,使用javap查看编译后的字节码文件。

主要看这三个方法,其他的省略,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  public synchronized void doSth1();
descriptor: ()V
flags: (0x0021) 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 Hello world!
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 3: 0
line 5: 8

public void doSth2();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: ldc #5 // class SyncDemo
2: dup
3: astore_1
4: monitorenter
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #3 // String Hello world!
10: invokevirtual #4 // 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: return
Exception table:
from to target type
5 15 18 any
18 21 18 any
LineNumberTable:
line 7: 0
line 8: 5
line 9: 13
line 10: 23
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 18
locals = [ class SyncDemo, class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4

public void doSth3();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello world!
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 12: 0
line 13: 8
}

首先看下doSth1()跟doSth()3的区别,仅仅只是在方法那里加了一个ACC_SYNCHRONIZED标志。

也就是说,对于同步方法,jvm采用ACC_SYNCHRONIZED关键字来实现同步。

JVM的解释翻译后,大概是这样子的:

方法级的同步是隐式的,在同步方法的常量池中会多了一个ACC_SYNCHRONIZED标志,当某个线程要访问这个方法时,会先检查是否存在该同步标志,如果存在,需要先获得监视器锁,然后执行,再然后释放监视器锁。如果获得不到锁,就需要等待,获取了监视器锁才可以执行下去。

值得注意的是,如果在同步方法中存在异常,在方法中又没有捕获处理,那么,在抛出该异常的时候,会先释放所占用的监视器锁。

而doSth2()就有很大的差异了。

首先没有额外的标记(ACC_SYNCHRONIZED)

其次,System.out.println(“Hello world!”);的关键代码被包围起来了,最主要的两句就是monitorenter,还有monitorexit。监视器进入,监视器退出。

每个对象都维护着一个计数器用于记录当前被锁的次数。当执行monitorenter后,该计数器会自增1,对应的线程获得锁,而执行monitorexit后,该计数器会自减1,对应的线程也会释放锁。在计数器字段为0的时候,都可以被任何线程获得锁,而当计数器字段不为0的时候,只有已经获得了锁的线程可以再次获得锁(重入锁)。

原文作者:dcLunatic

原文链接:http://dclunatic.github.io/synchronized%E5%85%B3%E9%94%AE%E5%AD%97.html

发表日期:September 21st 2018, 1:40:32 pm

更新日期:July 11th 2021, 9:13:50 pm

版权声明:转载的时候,记得注明来处

CATALOG
  1. 1. synchronized关键字