偏向锁和轻量级锁

来源:互联网 发布:最好用的优化软件 编辑:程序博客网 时间:2024/05/01 03:07

1我们先说下java 对象头部分的 Mark Word。在32位虚拟机上,他的长度是32位;在64位虚拟机上,他的长度是64位。

java1.6之后,对象有4中状态:无锁态,偏向锁态,轻量锁态,重量锁态。

普通情况下,Mark Word的格式如下:

hash:25 ————>| age:4 | biased_lock:1 lock:2

最后部分2个bit是固定的,表示对象锁的状况

01 表示无锁,或者偏向锁; 00表示轻量级锁;10表示重量级锁

第三部分是标识是否偏向锁。当最后部分为01时,第三部分为0表示普通无锁对象,第三部分为1表示偏向锁;最后部分为其他值,第三部分不存在。

age表示分代年龄;hash表示对象hash值。

普通无锁:对象hash:25 ————>| age:4 | 0 | 01

偏向锁:偏向锁的线程和时间戳:25 ————>| age:4 | 1 | 01

轻量锁:指向栈中锁记录的指针:30 | 00

重量锁:指向互斥量(重量级锁)的指针:30 | 10

偏向锁

为了让线程获取锁的代价减少,从而引入了偏向锁。当一个线程访问同步块并获取锁的时候,会在对象头和栈帧中的琐记录里存储锁偏向的线程ID,以后线程在进入同一个同步块时,不需要进行CAS操作,来加锁和解锁。

1. 加锁过程

先测试对象头的Mark Word里是否存储这指向当前线程的偏向锁。如果测试成功,表示线程已经获得锁。如果测试失败,需要再次测试对象头的Mark Word里的偏向锁标识是否设置为1(表示当前是偏向锁),如果设置了,则尝试进行CAS操作将对象头的偏向锁指向当前线程,如果没有设置,则采用CAS竞争锁。

2. 解锁过程

如果有其他的线程进行了锁请求,则持有偏向锁的线程才会释放锁。
偏向锁的撤销过程需要等待全局安全点,即该时刻没有正在执行的字节码。它会首先先暂停持有偏向锁的线程,然后检查持有偏向锁的线程是否还活着,如果线程不处与活动状态,则将对象头设置为无锁状态。如果线程还存活着,用于偏向锁的栈会先被执行,遍历偏向对象的琐记录,栈中和对象头里的Mark Word要么重新偏向其他线程,要么恢复到无锁状态或者标记对象不适合作为偏向锁,最后唤醒暂停的所有线程。

启用参数:
-XX:+UseBiasedLocking
关闭延迟:
-XX:BiasedLockingStartupDelay=0
禁用参数:
-XX:-UseBiastedLocking

轻量级锁

轻量级锁并不是用来代替重量级锁的,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用产生的性能消耗。在解释轻量级锁的执行过程之前,先明白一点,轻量级锁所适应的场景是线程交替执行同步块的情况,如果存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀为重量级锁。

1. 加锁过程

线程在执行同步块之前,JVM会先在线程的栈帧中创建一个存储锁记录的空间,并将对象头的Mark Word复制到琐记录中,官方成为Displaced Mark Word。然后线程尝试使用CAS将对象头中的Mark Word 替换为指向琐记录的指针。如果成功,当前线程获得锁。如果失败,说明有其他线程竞争锁,当前线程便尝试使用自旋来获得锁。

2. 解锁过程

解锁时会使用CAS操作,尝试将Displace Mark Word替换回到对象头中,如果成功,这说明没有竞争发生。如果失败,则说明当前锁存在竞争,这时轻量级锁就会膨胀为重量级锁。

重量级锁

由于自旋会消耗CPU资源,为了避免无用的自旋,当轻量级锁膨胀为重量级锁时,就不会再回到轻量级锁。当锁处于重量级时,其他线程尝试获取锁,都会被阻塞。当持有锁的线程释放锁后,会唤醒等待的线程,然后进行新一轮的竞争。

优缺点对比

锁 优点 缺点 使用场景 偏向锁 加锁和解锁不需要额外的消耗,和执行非同步方法相比,只有纳秒级别的差距 如果线程间存在竞争,会带来撤销锁的额外消耗 适用于只有一个线程访问同步块的场景 轻量级锁 竞争的线程不会阻塞,提高了相应速度 如果线程得不到锁会一直自旋,消耗CPU 追求相应时间;同步块代码执行速度快 重量级锁 线程竞争不使用自旋,不消耗CPU 线程阻塞,响应时间慢 追求吞吐量;同步块执行时间较长
0 0
原创粉丝点击