synchronized给同步资源加的锁就在Java对象头里;Hotspot的对象头主要包括两部分数据:Mark Word**(标记字段)、**class Pointer(类型指针)。
Mark Word:大小=xx位(JVM)bit,默认存储对象的HashCode,分代年龄和锁标志位信息。
锁状态 | 存储内容 | 标志位 | |||
---|---|---|---|---|---|
无锁 | 对象的hashCode、对象分代年龄、是否是偏向锁(0) | 01 | |||
偏向锁 | 偏向线程ID、偏向时间戳、对象分代年龄、是否是偏向锁(1) | 01 | |||
轻量级锁 | 指向栈中锁记录的指针 | 00 | |||
重量级锁 | 指向互斥量(重量级锁)的指针 | 10 |
class Pointer:数组类型对象的大小=2*xx位(JVM)bit,非数组类型对象的大小=xx位(JVM)bit,对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
synchronized 有4种锁的状态,这几个状态会随着竞争情况逐渐升级,锁状态只能升级不能降级
无锁:无锁没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。(CAS原理应用即是无锁的实现 )
偏向锁:偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价。
(偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令即可 )
轻量级锁:简单地将对象头部作为指针,指向持有锁的线程堆栈的内部,来判断一个线程是否持有对象锁
重量级锁:
综上,偏向锁通过对比Mark Word解决加锁问题,避免执行CAS操作。
轻量级锁是通过用CAS操作和自旋来解决加锁问题,避免线程阻塞和唤醒而影响性能。
重量级锁是将除了拥有锁的线程以外的线程都阻塞。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0LGG9N6Y-1571281513504)(D:\weixinobU7Vjs0UlDpUbXEpi4j3BBnesQQ\c199b46244b34bffb1d2876d645e952b\clipboard.png)]
偏向锁就是在无竞争的情况下把整个同步都消除掉,连CAS都不做了。
锁对象第一次被线程获取时,虚拟机将对象头的标志位设位“01”,即偏向模式。同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark word,如果CAS操作成功,持有偏向锁的线程每次进入到这个锁相关的同步块的时候,虚拟机不再进行任何同步操作。
当有另外一个线程获取这个锁时,偏向模式结束。根据锁对象目前是否处于被锁定状态,撤销偏向恢复到未锁定或轻量级锁定状态【如果线程到达安全点之后锁车厢结束,但是线程还在同步块中将会编程轻量级锁,否则变为无锁】
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X0NSXGmv-1571281513505)(D:\weixinobU7Vjs0UlDpUbXEpi4j3BBnesQQ\0084972a50ca4e2faa5e503ebd184be8\clipboard.png)]
代码进入到同步块的时候,如果此对象没有锁定,虚拟机首先在当前线程的栈帧中建立一个锁记录空间(Lock Record),用于存储对象目前的Mark Word 的拷贝;
然后使用CAS尝试将对象头的Mark Word 更新为指向Lock Record 的指针。如果更新成功,那么这个线程就拥有了该对象的锁,并且对象Mark word 的锁标志位转变为“00”。
如果更新失败,虚拟机首先检查对象的Mark Word 是否指向当前线程的栈帧,当指向,直接进入同步块执行,否则说明被其他线程抢占了,进行自旋等待。
当有第三个线程争用同一个锁时,轻量级锁膨胀为重量级锁,锁标志位变为“10”,Mark word 存储的是指向重量级锁的指针,后面等待锁的线程进入阻塞状态。
轻量级锁是在无竞争的情况下使用CAS操作去消除同步使用的互斥量。
的是指向重量级锁的指针,后面等待锁的线程进入阻塞状态。
轻量级锁是在无竞争的情况下使用CAS操作去消除同步使用的互斥量。