synchronized、monitor、wait、notify
Synchronize是java中解决并发问题比较常用的一种方法。从语法上面来说synchronized总共有三种用法。修饰普通方法,修饰静态方法,和同步代码块。

我们对同步代码块的方法进行反编译,可以发现,在synchronized囊括的代码中分别有monitorenter和monitorexit两个指令。虚拟机规范中对着两个指令如下描述:每个对象都有一个monitor。当monitor被占用的时候就会处于锁定状态。当线程monitorenter指令尝试获取monitor所有权时,如果进入数为0,则该线程进入,将进入数设置为1,该线程几位monitor的所有者。其他的线程在进入的时候就会进入阻塞状态,知道monitor的进入数量为0.而monitorexit会对进入数量减1,当变为0的时候,monitor就不再被这个线程占用。

而对synchronize修饰的方法反编译的时候发现,方法没有在使用monitorenter和monitorexit来实现,但是在方法的flags中acc——synchronized的标志。当调用acc_synchronize标志的方法的时候也会先去获取monitor对象,所以本质上和同步代码块是一样的。

同步代码块的monitor来自指定对象,同步方法来自当前实例,而静态方法来自class对象。

在java中每个对象都有monitor。对象在内存中的布局可以分为3个区块,对象头,实例数据和对齐填充。其中对象头保存了锁状态标识,线程持有锁等相关的数据。

而对象的wait和notify的方法也是通过monitor来实现的,一个持有对象线程通过调用对象的wait方法,释放该对象的monitor并且进入休眠,其他的线程通过对象对的notify和notifyall再次获取monitor。所以wait和notify都需要持有对象的monitor才能调用,否则将抛出IllegalMonitorStateException。这也是wait和notify需要在同步的方法中的原因。

synchronized

1.synchronized锁,可分为偏向锁、轻量级锁、重量级锁。在jvm没有显示关闭偏向锁的情况下,初始状态时默认是偏向锁时,
线程请求先通过CAS替换mark word中threadId,如果替换成功则该线程持有当前锁。如果替换失败,锁会升级为轻量级锁,
线程请求会尝试CAS替换mark word中指向栈中锁记录的指针,如果替换成功则该线程持有当前锁。
如果替换失败,当前线程会自旋一定次数,继续尝试获取CAS替换,如果超过一定自旋次数,锁升级为重量级锁。

synchronized锁是调用系统内核互斥锁实现的,线程在获取synchronized锁失败后,也会进入一个等待获取锁队列中(系统内核实现的),
线程会由运行态切换到阻塞态,让出CPU,待其他线程释放锁后唤醒它。

synchronize锁重(1.6之后jvm有优化)就是重在两点,一是调用内核互斥锁实现,二是线程获取锁失败会变成阻塞态,让出CPU,等待唤醒(有一定的上下文切换)

https://juejin.im/post/5b4eec7df265da0fa00a118f#heading-12