synchronized关键字
一、语义
1、当线程释放锁时,JMM会把本地内存中的数据刷新到主内存中。
2、当获取锁时,会把本地内存设置为无效,从主内存中读取数据。
二、synchronized对象
对于synchronized的同步,锁是java中的对象。
1、对于实例方法,锁是当前对象的实例。
2、对于静态方法,锁是该类的Class对象。
3、对于同步代码块,锁是synchronized后面跟的对象。
JVM基于进入和退出monitor对象来实现synchronized代码块的。代码块同步是使用monitorenter 和monitorexit指令实现的。在java中,每一个对象都与一个minitor相对应。当且一个monitor被持有后,它处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。
三、JAVA对象头
1、如果java对象是一个数组,那么java对象头用三个字来表示
2、如果java对象不是数组,那么用两个字来表示
在32位虚拟机中,一个字长位32位。64位虚拟机中,一个字长是64位。
在java对象头中,第一个字为markword,默认存储对象的分代年龄,hashcode和锁标志位。其中的数据并会随着锁标志位的变化而变化。
第二个字即为CLass Metadata Address 即为对象类型数据的指针。
第三个字(如有),为数组长度
四、MarkWord
java对象头的第一个字。用来存储对象的分代年龄,hashcode和锁标志位等。
markWord会随着锁标志位的变化而变化。
五、锁升级过程
synchronized锁一共有4种状态,级别从低到高分别是无锁,偏向锁,轻量级锁,重量级锁。锁只能升级而不能降级。
1、偏向锁
偏向锁的作用是当只有一个线程经常访问锁的时候,通过比较线程ID而不是使用cas来获得锁。
1、偏向锁在对象头和栈帧中引入了线程ID。
2、当一个线程访问同步块时,如果是无锁状态,首先会用CAS操作在markword和对象头中设置了自己的线程ID,并将锁的标志位改为偏向锁。
3、当再次进入此代码块时只需要检查当前的markword中的锁标志位是否是偏向锁,线程ID是否为当前线程,如果是,直接获得锁。
4、如果不是,则使用CAS操作尝试将线程ID设置为自己的线程ID。
2、偏向锁的撤销。
只有当其他线程尝试获得偏向锁时,偏向锁才会被释放。偏向锁的撤销是在全局安全点时进行的。此时没有正在执行的字节码。
1、暂停持有该偏向锁的线程,并检查该线程是否存活。
2、若不处于活动状态,则将对象头设置为无锁状态。
3、若处于活动状态,Mark Word要么重新偏向于其他线程,要么恢复到无锁或者标记对象不适合作为偏向锁。
4、恢复被暂停的线程。
偏向锁可以提高同步但无竞争的程序性能,但是如果程序中的大多数锁都是被多个不同的线程访问,那么偏向锁就是多余的。可以禁止偏向锁来优化性能(减少锁撤销的开销)。
因为偏向锁带来的性能优化不如之前,并且将大量复杂代码引入到了同步子系统中,也侵害了VM中的其他组件。以后将禁用,弃用并最终取消对偏向锁定的支持。参见https://bugs.openjdk.java.net/browse/JDK-8231265。
3、轻量级锁
作用是在没有多线程竞争的情况下,减少重量级锁使用操作系统互斥量带来的性能消耗。
线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,称为Displaced Mark Word。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。如果自旋失败,则升级为重量级锁。
4、重量级锁
使用操作系统信号量来实现。
来源:CSDN
作者:我家璨璨真可爱
链接:https://blog.csdn.net/weixin_42379740/article/details/103458108