每个对象的对象头部都有一个markword,而锁的升级过程体现在对象的markwork中的数据变化
以jvm 64位环境为例
锁的状态分为以下四种:
锁状态 | 内容 |
---|---|
无锁 | 此时对象内有他的hashcode和年龄代以及偏向锁的标志位和锁的标志位 |
偏向锁 | 偏向锁记录了上一个持有锁的线程id或者此时正在持有锁的线程id和锁的标志位 |
轻量级锁 | 记录了lockrecord的指针 |
重量级锁 | 记录了重量级锁的指针 |
下面对锁升级的过程进行简述
1,无锁状态不叙述
2.当这个对象从来没被线程占用(注意是从来没被线程拥有过),线程A进来了,A通过CAS操作,将线程id的位置贴上自己的线程id,并将偏向锁的位置由0改为1,进而执行自己的代码,A执行完毕退出
2.1 线程A 第二次想要持有这个锁,他发现线程id是自己的,这个时候就不需要执行CAS.直接占有锁,执行自己的代码,A执行完毕后退出
2.2 线程B 来了,想持有这个锁,那么他发现里面有线程id并且不是自己的,那么就通过CAS将线程id更改为自己的,并且执行自己的代码
3.如果A和B同时来了,抢占这个锁,那么说明这个锁有竞争了,此时撤销偏向锁,升级为轻量级锁,同时,线程在自己的栈内开辟一个空间lock record,使用CAS将自己的record地址贴在锁的markword中,谁贴上了,谁就抢占了锁,锁状态改为 00,假设A抢占了锁,那么线程B就开始自旋
4.线程B自旋的次数根据jvm来定(jdk1.6之前是10次),如果自旋还没有获取到锁,那么升级为重量级锁,线程阻塞,对象内的指针指向重量级锁指针
为什么要记录线程id,每次都CAS替换线程id不好吗。
因为CAS操作是原子操作比较耗时,而如果只有一个线程一直访问锁模块,那么就只需要判断不需要原子操作,减少执行的时间。
为什么锁需要升级,不能直接使用重量级锁?
重量级锁比较耗时,
因为轻量级锁转为重量级锁的过程为用户态转为内核态的过程,涉及到cpu操作时间会比内存操作时间长。
用户态:用户代码的操作
内核态:cpu的相关操作
个人理解,如有问题请指出,一定改正