管程
管程是一种通用的同步原语,在 Java 中指的就是 synchronized,synchronized 是 Java 里对管程的实现。
对象的状态
如果一个对象里面有可以被修改的成员变量,那么就称这个对象为有状态的对象。反之就叫做无状态的对象。
临界区

Synchornized 语言的层面实现锁
- 修饰实例方法, 锁是当前的对象
- 修饰静态方法,锁是当前的类对象
- 修饰代码块, 锁是手动指定的对象(任何一个Object都行)
- 如果一个对象有若干个Synchornized方法,在某一个时刻,这些方法中只能有某一个Synchornized方法被某一个线程所访问,其他的所有线程访问该对象的Synchornized方法都会被等待。因为当前的对象只有一把锁。
- static 与 synchornized 同时存在时,该锁不再是当前对象的锁,而是当前对象所对应的类的class对象的锁。
:::info
提问:两个线程能同时执行同一个对象的下面两个方法吗?

答:是可以的,因为这两个方法的锁是不同的,method1 的锁是当前Test 对象,method2的锁是这个Test类锁对应的class对象。 :::
synchronized字节码
monitorenter与monitorexit指令


当我们使用synchronized关键字来修饰代码块时,字节码层面上是通过monitorenter与monitorexit指令来实现的锁的获取与释放动作。当线程进入到monitorenter指令后,线程将会持有Monitor对象,退出monitorenter指令后,线程将会释放Monitor对象。monitorenter与**monitorexit可能是一对多的关系。
monitorenter 指令插入到同步代码块开始的位置,monitorexit **指令插入到同步代码块结束位置,jvm 需要保证每个monitorenter 都有一个 monitorexit 对应。这两个指令隐式的执行了 Lock 和 UnLock 操作
这两个指令,本质上都是对一个对象的监视器(monitor)进行获取,这个过程是排他的,也就是说同一时刻只能有一个线程获取到由 synchronized 所保护对象的监视器。
线程执行到 monitorenter 指令时,会尝试获取对象所对应的 monitor 所有权,也就是尝试获取对象的锁,而执行monitorexit,就是释放 monitor 的所有权。为什么会有两个monitorexit ?
为了应对异常情况的发生而产生的一条字节码指令。synchronized代码块主动抛出异常的字节码
主动抛出异常,这时候javap 会发现只有一个monitorexit。因为这段代码只会有一个异常出口。public void method() {synchronized (object) {System.out.println("hello world");throw new RuntimeException();//主动抛出异常}}
synchronized修饰实例方法的字节码
public synchronized void method() {System.out.println("hello world");}
发现方法会多一个ACC_SYNCHRONIZED的标志位。
对于synchronized关键字修饰方法来说,并没有出现monitorenter与monitorexit指令**,而是出现了一个ACC_SYNCHRONIZED标志。JVM使用了ACC_SYNCHRONIZED访问标志来区分一个方法是否为同步方法**;当方法被调用时,调用指令会检查该方法是否拥有ACC_SYNCHRONIZED标志,如果有,那么执行线程将会先持有方法所在对象的Monitor对象,然后再去执行方法体;在该方法执行期间,其他任何线程均无法再获取到这个Monitor对象,当线程执行完该方法后,它会释放掉这个Monitor对象。
synchronized修饰静态方法的字节码

args_size=0,表示并没有传入this参数

