其他分享
首页 > 其他分享> > (4.6)轻量级锁(锁膨胀、锁自选、偏向锁、锁消除)

(4.6)轻量级锁(锁膨胀、锁自选、偏向锁、锁消除)

作者:互联网

4.6 轻量级锁、偏向锁——Monitor升级

JDK6之前的加锁方式是:关联锁对象到Monitor进行加锁,Monitor是由操作系统提供的,加锁代价高。

JDK6之后,对加锁方式进行了优化,引入了轻量级锁、偏向锁等。

1. 轻量级锁

如果一个对象虽然有多个线程要加锁,但是加锁的时间是错开的(没有竞争),可以使用轻量级锁来优化。

如果这时有其他线程来获取轻量级锁,则被阻塞,加锁线程将轻量级锁升级为Monitor锁

假设有两个方法同步块,利用同一个对象加锁

private static final Object lock = new Object();

public static void method1() {
        synchronized (lock) {
            log.info("method1....");
            method2();
        }
}

private static void method2() {
        synchronized (lock) {
            log.info("method2...");
        }
}

2. 锁膨胀

线程尝试CAS加轻量级锁时,失败,如果情况是有其他线程已经对锁对象加上了轻量级锁,则进行锁膨胀,将轻量级锁变为重量级锁。

3. 锁自旋

重量级锁竞争时,竞争线程进入EntryList阻塞前,还可以使用自旋来优化,如果自旋成功,就避免了阻塞带来的上下文切换。

JDK6的自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会 高,就多自旋几次;反之,就少自旋甚至不自旋。

JDK7之后无法控制是否开启自旋锁。

优点:

缺点:

4. 偏向锁

当轻量级锁(没有竞争,就自己这个线程)+ 锁重入时,每次都需要生成锁记录,并尝试CAS替换对象头的MarkWord操作。

JDK6引入偏向锁做进一步优化: 只有第一次加锁时,不需要生成锁记录,将线程ID设置到锁对象的MarkWord中;之后锁重入时,检查MarkWord是否是自己的线程ID,只要不发生竞争,则可以一直使用偏向锁。

优点:避免了每次加锁都需要生成锁记录,并CAS的过程。

5. 撤销偏向锁

(1)调用hashcode

调用hashcode会撤销偏向锁,因为偏向锁的锁对象MarkWord存储的是线程ID,调用偏向锁会导致偏向锁被撤销。

(2) 其他线程交错使用锁对象

会将偏向锁升级为轻量级锁

(3) 其他线程竞争使用锁对象

将偏向锁升级为重量级锁

(4) 调用wait/notify

wait、notify是重量级锁才有的机制,调用它们会将偏向锁升级为重量级锁

6. 批量重偏向

锁对象偏向线程1,这时有线程2交替获取锁,会撤销包含线程1ID的偏向锁(线程ID变为线程2ID,释放锁后,偏向锁ID恢复为线程1ID);

当阈值超过20此后,会在加锁时重新偏向线程2(即将锁对象的线程ID替换为线程2的ID)

7. 批量撤销

当撤销偏向锁次数超过40,JVM意识到重偏向也不对,于是整个类的所有对象都变为不可偏向锁,新创建的对象也是不可偏向锁。

8. 锁消除

如果锁对象是局部的、不同享的、没有竞争的,则JIT即时编译器会对代码进行优化,执行时不加锁。提高运行效率。

锁消除默认开启,关闭锁消除:

java -XX:-EliminateLocks -java HelloConcurrent.jar

手动开启锁消除

java -XX:EliminateLock -java HelloConcurrent.jar

标签:MarkWord,4.6,对象,加锁,线程,自选,轻量级,偏向
来源: https://www.cnblogs.com/greengages/p/16620550.html