IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 锁优化(锁升级、锁粗化、锁消除) -> 正文阅读

[系统运维]锁优化(锁升级、锁粗化、锁消除)

锁优化

阿里开发手册

image-20210908204838914

synchronized 锁优化的背景

用锁能够实现数据的安全性,但是会带来性能下降。
无锁能够基于线程并行提升程序性能,但是会带来安全性下降。

要在性能与安全找到平衡点:

jdk6引入偏向锁、轻量级锁

Synchronized的性能变化

Java5之前,用户态和内核态之间的切换

java5以前,只有Synchronized,这个是操作系统级别的重量级操作

重量级锁,假如锁的竞争比较激烈的话,性能下降‘

image-20210908205716396

java的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程就需要操作系统介入,需要在户态与核心态之间切换,这种切换会消耗大量的系统资源,因为用户态与内核态都有各自专用的内存空间,专用的寄存器等,用户态切换至内核态需要传递给许多变量、参数给内核,内核也需要保护好用户态在切换时的一些寄存器值、变量等,以便内核态调用结束后切换回用户态继续工作。

在Java早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的Mutex Lock来实现的,挂起线程和恢复线程都需要转入内核态去完成,阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态切换需要耗费处理器时间,如果同步代码块中内容过于简单,这种切换的时间可能比用户代码执行的时间还长”,时间成本相对较高,这也是为什么早期的synchronized效率低的原因
Java 6之后,为了减少获得锁和释放锁所带来的性能消耗,引入了轻量级锁和偏向锁

java6开始,优化Synchronized

Java SE 1.6为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”,在Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级不能降级的策略,目的是为了提高获得锁和释放锁的效率,

image-20210908205452654

synchronized锁:由对象头中的Mark Word根据锁标志位的不同而被复用及锁升级策略

锁升级

synchronized用的锁是存在Java对象头里的Mark Word中
锁升级功能主要依赖MarkWord中锁标志位和释放偏向锁标志位

锁的4种状态:无锁状态、偏向锁状态、轻量级锁状态(自旋锁。自适应自旋锁)、重量级锁状态(级别从低到高)

锁机制就是根据资源竞争的激烈程度不断进行锁升级的过程

image-20210908214211133

image-20210904182231892

无锁

maven引入JOL

<!--JOL   Java Object Layout Java对象布局-->
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
</dependency>
package com.dongguo.lockupgrade;

import org.openjdk.jol.info.ClassLayout;

/**
 * @author Dongguo
 * @date 2021/9/8 0008-21:46
 * @description: 无锁
 */
public class MyObject {
    public static void main(String[] args) {
        Object o = new Object();

        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
}
运行结果
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

01 00 00 00 (00000001 00000000 00000000 00000000) (1)

00000001 00000000 00000000 00000000 二进制需要倒着看(每8个位看做一个整体) 1 2 3 4 变成 4 3 2 1

00000000 00000000 00000000 00000(001)

image-20210908215530307

此时hashcode是0

因为懒加载的缘故,使用到hashcode才会初始化

package com.dongguo.lockupgrade;

import org.openjdk.jol.info.ClassLayout;

/**
 * @author Dongguo
 * @date 2021/9/8 0008-21:46
 * @description:
 */
public class MyObject {
    public static void main(String[] args) {
        Object o = new Object();
        System.out.println("10进制hash码:"+o.hashCode());
        System.out.println("16进制hash码:"+Integer.toHexString(o.hashCode()));
        System.out.println("2进制hash码:"+Integer.toBinaryString(o.hashCode()));

        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
}
运行结果
10进制hash码:1265094477
16进制hash码:4b67cf4d
2进制hash码:1001011011001111100111101001101
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 4d cf 67 (00000001 01001101 11001111 01100111) (1741638913)
      4     4        (object header)                           4b 00 00 00 (01001011 00000000 00000000 00000000) (75)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

00000001 01001101 11001111 01100111 01001011

倒着看 31位

0(1001011 01100111 11001111 01001101) 00000001

2进制hash码: 1001011011001111100111101001101

? 0(1001011 01100111 11001111 01001101) 00000001

去掉后8位

01001101 11001111 01100111 01001011

hashcode是31位

1001101 11001111 01100111 01001011

偏向锁:

偏向锁偏向于第一个获得它的线程,默认不存在锁竞争的情况下,常常是一个线程多次获得同一个锁,重复获取同一把锁不会再进行锁的竞争,

看看多线程卖票,同一个线程获得体会一下

package com.dongguo.lockupgrade;

/**
 * @author Dongguo
 * @date 2021/9/3 0003-10:14
 * @description: 实现3个售票员卖出50张票的案例
 */
public class SaleTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(() -> {
            //循环100次保证能够卖光票
            for (int i = 0; i < 100; i++) {
                ticket.saleTicket();
            }
        }, "T1").start();
        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                ticket.saleTicket();
            }
        }, "T2").start();
        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                ticket.saleTicket();
            }
        }, "T3").start();
    }
}

/**
 * @author Dongguo
 * @description: 资源类
 */
class Ticket {
    private int count = 50;

    public synchronized void saleTicket() {
        if (count > 0) {
            count--;
            System.out.println(Thread.currentThread().getName() + "卖票成功,还剩" + count + "张票!");
        }
    }
}
运行结果
T1卖票成功,还剩49张票!
T1卖票成功,还剩48张票!
T1卖票成功,还剩47张票!
T1卖票成功,还剩46张票!
T1卖票成功,还剩45张票!
T1卖票成功,还剩44张票!
T1卖票成功,还剩43张票!
T1卖票成功,还剩42张票!
T1卖票成功,还剩41张票!
T1卖票成功,还剩40张票!
T1卖票成功,还剩39张票!
T1卖票成功,还剩38张票!
T1卖票成功,还剩37张票!
T1卖票成功,还剩36张票!
T1卖票成功,还剩35张票!
T1卖票成功,还剩34张票!
T1卖票成功,还剩33张票!
T1卖票成功,还剩32张票!
T1卖票成功,还剩31张票!
T1卖票成功,还剩30张票!
T1卖票成功,还剩29张票!
T1卖票成功,还剩28张票!
T1卖票成功,还剩27张票!
T1卖票成功,还剩26张票!
T1卖票成功,还剩25张票!
T1卖票成功,还剩24张票!
T1卖票成功,还剩23张票!
T1卖票成功,还剩22张票!
T1卖票成功,还剩21张票!
T1卖票成功,还剩20张票!
T1卖票成功,还剩19张票!
T1卖票成功,还剩18张票!
T1卖票成功,还剩17张票!
T1卖票成功,还剩16张票!
T1卖票成功,还剩15张票!
T1卖票成功,还剩14张票!
T1卖票成功,还剩13张票!
T1卖票成功,还剩12张票!
T1卖票成功,还剩11张票!
T1卖票成功,还剩10张票!
T1卖票成功,还剩9张票!
T1卖票成功,还剩8张票!
T1卖票成功,还剩7张票!
T1卖票成功,还剩6张票!
T1卖票成功,还剩5张票!
T1卖票成功,还剩4张票!
T1卖票成功,还剩3张票!
T1卖票成功,还剩2张票!
T1卖票成功,还剩1张票!
T1卖票成功,还剩0张票!

发现全是t1卖出

这样就是偏向锁的情况

为什么要引入偏向锁?

Hotspot 的作者经过研究发现,大多数情况下:

多线程的情况下,锁不仅不存在多线程竞争,还存在锁由同一线程多次获得的情况,

为了让线程获得锁的代价更低而引入了偏向锁。当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。如果测试成功,表示线程已经获得了锁。如果测试失败,则需要再测试一下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁):如果没有设置,则使用CAS竞争锁;如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。

偏向锁就是在这种情况下出现的,它的出现是为了解决只有在一个线程执行同步时提高性能。

即为了降低获取锁的代价,才引入的偏向锁。

image-20210908232852044

通过CAS方式修改markword中的线程ID

偏向锁的持有

理论落地:
在实际应用运行过程中发现,“锁总是同一个线程持有,很少发生竞争”,也就是说锁总是被第一个占用他的线程拥有,这个线程就是锁的偏向线程。
那么只需要在锁第一次被拥有的时候,记录下偏向线程ID。这样偏向线程就一直持有着锁(后续这个线程进入和退出这段加了同步锁的代码块时,不需要再次加锁和释放锁。而是直接比较对象头里面是否存储了指向当前线程的偏向锁)。
如果相等表示偏向锁是偏向于当前线程的,就不需要再尝试获得锁了,直到竞争发生才释放锁。以后每次同步,检查锁的偏向线程ID与当前线程ID是否一致,如果一致直接进入同步。无需每次加锁解锁都去CAS更新对象头。如果自始至终使用锁的线程只有一个,很明显偏向锁几乎没有额外开销,性能极高。
假如不一致意味着发生了竞争,锁已经不是总是偏向于同一个线程了,这时候可能需要升级变为轻量级锁,才能保证线程间公平竞争锁。偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程是不会主动释放偏向锁的。

技术实现:
一个synchronized方法被一个线程抢到了锁时,那这个方法所在的对象就会在其所在的Mark Word中将偏向锁修改状态位,同时还会有占用前54位来存储线程指针作为标识。若该线程再次访问同一个synchronized方法时,该线程只需去对象头的Mark Word 中去判断一下是否有偏向锁指向本身的ID,无需再进入 Monitor 去竞争对象了。

举例说明

偏向锁的操作不用直接捅到操作系统,不涉及用户到内核转换,不必要直接升级为最高级,我们以一个account对象的“对象头”为例,

image-20210908233245396

假如有一个线程执行到synchronized代码块的时候,JVM使用CAS操作把线程指针ID记录到Mark Word当中,并修改标偏向标示,标示当前线程就获得该锁。锁对象变成偏向锁(通过CAS修改对象头里的锁标志位),字面意思是“偏向于第一个获得它的线程”的锁。执行完同步代码块后,线程并不会主动释放偏向锁。

image-20210908233253586

这时线程获得了锁,可以执行同步代码块。当该线程第二次到达同步代码块时会判断此时持有锁的线程是否还是自己(持有锁的线程ID也在对象头里),JVM通过account对象的Mark Word判断:当前线程ID还在,说明还持有着这个对象的锁,就可以继续进入临界区工作。由于之前没有释放锁,这里也就不需要重新加锁。 如果自始至终使用锁的线程只有一个,很明显偏向锁几乎没有额外开销,性能极高。

结论:JVM不用和操作系统协商设置Mutex(争取内核),它只需要记录下线程ID就标示自己获得了当前锁,不用操作系统接入。
上述就是偏向锁:在没有其他线程竞争的时候,一直偏向偏心当前线程,当前线程可以一直执行。

偏向锁JVM命令

查出BiasedLock相关的参数设置

java -XX:+PrintFlagsInitial |grep BiasedLock*

linux命令

image-20210908234401983

windows cmd命令

image-20210909082534843

默认偏向锁是打开的 UseBiasedLocking = true

但是BiasedLockingStartupDelay =4000 偏向锁 启动时间有延迟,

* 实际上偏向锁在JDK1.6之后是默认开启的,但是启动时间有延迟,
* 如有必要可以使用JVM参数来关闭延迟
* 所以需要添加参数-XX:BiasedLockingStartupDelay=0,让其在程序启动时立刻启动。
*
* 开启偏向锁:
* -XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
*
* 如果你确定应用程序里所有的锁通常情况下处于竞争状态,可以通过JVM参数关闭偏向锁
* 关闭偏向锁:关闭之后程序默认会直接进入------------------------------------------>>>>>>>>   轻量级锁状态。那么程序默认会进入轻量级锁状态
* -XX:-UseBiasedLocking

1使用默认设置 默认有延迟时间

package com.dongguo.lockupgrade;

import org.openjdk.jol.info.ClassLayout;

/**
 * @author Dongguo
 * @date 2021/9/8 0008-21:46
 * @description:
 */
public class MyObject {
    public static void main(String[] args) {
        Object object = new Object();

        new Thread(() -> {
            synchronized (object) {
                System.out.println(ClassLayout.parseInstance(object).toPrintable());
            }
        }, "t1").start();

    }
}
运行结果
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           60 f6 ef cd (01100000 11110110 11101111 11001101) (-839911840)
      4     4        (object header)                           b5 00 00 00 (10110101 00000000 00000000 00000000) (181)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

由于延迟4秒钟,锁信息显示出来的是不正确的 后8位字节01100000,是轻量级锁状态

2设置延迟时间为0 -XX:BiasedLockingStartupDelay=0

package com.dongguo.lockupgrade;

import org.openjdk.jol.info.ClassLayout;

/**
 * @author Dongguo
 * @date 2021/9/8 0008-21:46
 * @description:
 */
public class MyObject {
    public static void main(String[] args) {
        Object object = new Object();

        new Thread(() -> {
            synchronized (object) {
                System.out.println(ClassLayout.parseInstance(object).toPrintable());
            }
        }, "t1").start();
    }
}
运行结果
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 70 f3 b2 (00000101 01110000 11110011 10110010) (-1292668923)
      4     4        (object header)                           2a 02 00 00 (00101010 00000010 00000000 00000000) (554)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

后8位00000101 锁信息 101 即偏向锁

1表示为偏向锁位

01表示为锁标记位

image-20210908232852044

偏向锁的升级

(一个线程持有锁,第二个线程加入锁竞争)

如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会将偏向锁升级为轻量级锁。

偏向锁的的撤销

当有另外线程逐步来竞争锁的时候,就不能再使用偏向锁了,要升级为轻量级锁

竞争线程尝试CAS更新对象头失败,会等待到全局安全点(STW,此时不会执行任何代码)撤销偏向锁。

偏向锁使用一种等到竞争出现才释放锁的机制,只有当其他线程竞争锁时,持有偏向锁的原来线程才会释放锁。
偏向锁的撤销需要等待全局安全点(在这个时间点上没有正在执行的字节码 ,JVM中的STW的概念 ),

它会首先暂停拥有偏向锁的线程, 同时检查持有偏向锁的线程是否还在执行:

① 第一个线程正在执行synchronized方法(处于同步块),它还没有执行完,其它线程来抢夺,暂停第一个线程,该偏向锁会被取消掉并出现锁升级。
此时轻量级锁由原持有偏向锁的线程持有,继续执行其同步代码,而正在竞争的线程会进入自旋等待获得该轻量级锁。
② 第一个线程执行完成synchronized方法(退出同步块),第二个线程执行重新偏向 。

如果第一个线程退出,当第一个线程不存在了,第二个线程执行,CAS将当前线程指针修改,仍为偏向锁状态

如果第一个线程退出还要再次执行,第二个线程也要执行,那么两个线程就抢夺轻量级锁,没抢到的进行自旋

img

此时升级为轻量级锁

调用对象 hashCode导致偏向锁撤销

调用了对象的 hashCode,但偏向锁的对象 MarkWord 中存储的是线程 id,如果调用 hashCode 会导致偏向锁被撤销变为无锁状态
轻量级锁会在锁记录中记录 hashCode
重量级锁会在 Monitor 中记录 hashCode

在获得偏向锁后,调用 hashCode

package com.dongguo.lockupgrade;

import org.openjdk.jol.info.ClassLayout;

/**
 * @author Dongguo
 * @date 2021/9/11 0011-18:06
 * @description: -XX:BiasedLockingStartupDelay=0
 */
public class RedoLockDemo {
    public static void main(String[] args) {
        Object object = new Object();

        new Thread(() -> {
            synchronized (object) {
                System.out.println(ClassLayout.parseInstance(object).toPrintable());
            }
            object.hashCode();
            System.out.println(ClassLayout.parseInstance(object).toPrintable());
        }, "t1").start();
    }
}
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 f8 5d dd (00000101 11111000 01011101 11011101) (-581044219)
      4     4        (object header)                           4b 02 00 00 (01001011 00000010 00000000 00000000) (587)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 ea 67 be (00000001 11101010 01100111 10111110) (-1100486143)
      4     4        (object header)                           66 00 00 00 (01100110 00000000 00000000 00000000) (102)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

101 偏向锁变为001无锁

调用 wait/notify升级为重量级锁

如果线程获得锁后调用Object#wait方法,则会将线程加入到WaitSet中,当被Object#notify唤醒后,会将线程从WaitSet移动到cxq或EntryList中去。

一个ObjectMonitor对象包括这么几个关键字段:cxq(下图中的ContentionList),EntryList ,WaitSet,owner。

其中cxq ,EntryList ,WaitSet都是由ObjectWaiter的链表结构,owner指向持有锁的线程。

img

需要注意的是,当调用一个锁对象的waitnotify方法时,如当前锁的状态是偏向锁或轻量级锁则会先膨胀成重量级锁

package com.dongguo.lockupgrade;


import org.openjdk.jol.info.ClassLayout;

import java.util.concurrent.TimeUnit;

/**
 * @author Dongguo
 * @date 2021/9/11 0011-18:06
 * @description:
 */
public class RedoLockDemo {
    public static void main(String[] args) {
        Object object = new Object();

        Thread t1 = new Thread(() -> {
            synchronized (object) {
                try {
                    System.out.println(ClassLayout.parseInstance(object).toPrintable());
                    object.wait();
                    System.out.println("被唤醒");

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(ClassLayout.parseInstance(object).toPrintable());

        }, "t1");
        t1.start();
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (object) {
                object.notifyAll();
                System.out.println("notifyAll");
            }
        }, "t2").start();
    }
}
运行结果
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 40 22 62 (00000101 01000000 00100010 01100010) (1646411781)
      4     4        (object header)                           ec 01 00 00 (11101100 00000001 00000000 00000000) (492)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

notifyAll
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           da 9d 38 61 (11011010 10011101 00111000 01100001) (1631100378)
      4     4        (object header)                           ec 01 00 00 (11101100 00000001 00000000 00000000) (492)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total  

从101偏向锁变为010重量级锁

image-20210909093508534

批量重偏向与批量撤销

通过JVM的默认参数值,找一找批量重偏向和批量撤销的阈值。

设置JVM参数-XX:+PrintFlagsFinal,在项目启动时即可输出JVM的默认参数值

intx BiasedLockingBulkRebiasThreshold = 20 默认偏向锁批量重偏向阈值

intx BiasedLockingBulkRevokeThreshold = 40 默认偏向锁批量撤销阈值

当然我们可以通过-XX:BiasedLockingBulkRebiasThreshold 和 -XX:BiasedLockingBulkRevokeThreshold 来手动设置阈值

img

批量重偏向

如果对象虽然被多个线程访问,但没有竞争,这时偏向了线程 t1 的对象仍有机会重新偏向t2,重偏向会重置对象的 Thread ID
当撤销偏向锁阈值超过 20 次后,jvm 会这样觉得,我是不是偏向错了呢,于是在接下来的访问中,会在给这些对象加锁时重新偏向至加锁线程 t2

例如当一个线程t1创建了大量Dog对象并执行了初始的同步操作,后来另一个线程t2也将这些Dog对象作为锁对象进行操作,

注意,由于要输出多次JOL打印的二进制信息,这里我将jol的源码做了些修改,新增了只输出简化的二进制信息toPrintable2()并且修正了二进制打印的顺序,然后再打成jar包在本地引入

package com.dongguo.lockupgrade;


import org.openjdk.jol.info.ClassLayout;

import java.util.Vector;
import java.util.concurrent.TimeUnit;

/**
 * @author Dongguo
 * @date 2021/9/11 0011-18:06
 * @description: -XX:BiasedLockingStartupDelay=0
 *这里我将jol的源码新增了只输出简化的二进制信息toPrintable2()并且修正了二进制顺序,然后再打成jar包在本地引入
 */
class Dog {
}

public class RedoLockDemo2 {
    public static void main(String[] args) {
        Vector<Dog> list = new Vector<>();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                Dog d = new Dog();
                list.add(d);
                synchronized (d) {
                    System.out.println(ClassLayout.parseInstance(d).toPrintable2());
                }
            }
            synchronized (list) {
                list.notify();//唤醒t2
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(() -> {
            synchronized (list) {
                try {
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            for (int i = 0; i < 30; i++) {
                Dog d = list.get(i);
                System.out.println(i + "加锁前  " + ClassLayout.parseInstance(d).toPrintable2());
                synchronized (d) {
                    System.out.println(i + "加锁中  " + ClassLayout.parseInstance(d).toPrintable2());
                }
                System.out.println(i + " 加锁后 " + ClassLayout.parseInstance(d).toPrintable2());
            }
        }, "t2");
        t2.start();
    }
}

运行结果

00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
0加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
0加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
0 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
1加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
1加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
1 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
2加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
2加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
2 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
3加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
3加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
3 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
4加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
4加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
4 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
5加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
5加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
5 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
6加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
6加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
6 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
7加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
7加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
7 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
8加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
8加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
8 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
9加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
9加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
9 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
10加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
10加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
10 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
11加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
11加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
11 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
12加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
12加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
12 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
13加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
13加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
13 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
14加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
14加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
14 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
15加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
15加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
15 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
16加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
16加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
16 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
17加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
17加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
17 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
18加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
18加锁中  00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000 
18 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
19加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
19加锁中  00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
19 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
20加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
20加锁中  00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
20 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
21加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
21加锁中  00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
21 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
22加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
22加锁中  00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
22 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
23加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
23加锁中  00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
23 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
24加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
24加锁中  00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
24 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
25加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
25加锁中  00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
25 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
26加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
26加锁中  00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
26 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
27加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
27加锁中  00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
27 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
28加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
28加锁中  00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
28 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
29加锁前  00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101 
29加锁中  00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 
29 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101 

可以看出当t1线程执行时,30次循环将这30个dog对象的线程指针指向t1,前54位为t1的Thread ID

00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101

当t2线程执行时,第一次循环 加锁前dog对象0偏向t1线程

此时t1线程还存在,加锁时将偏向锁升级为轻量级锁

加锁后释放锁重置为无锁状态

0加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
0加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
0 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001

intx BiasedLockingBulkRebiasThreshold = 20 默认偏向锁批量重偏向阈值

当循环执行到20次时 将dog对象19的偏向指向t1修改为直接指向t2,就不再变为轻量级锁仍为偏向锁

达到偏向锁批量重偏向的阈值,之后的dog对象全都变为重偏向t2,而不再是t1

18加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
18加锁中 00000000 00000000 00000000 00011010 11100001 10101111 11110001 00100000
18 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
19加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
19加锁中 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
19 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101

20加锁前 00000000 00000000 00000001 11100101 11100000 11011110 00001000 00000101
20加锁中 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101
20 加锁后 00000000 00000000 00000001 11100101 11100000 11011110 01111001 00000101

批量撤销

当撤销偏向锁阈值超过 40 次后,jvm 会这样觉得,自己确实偏向错了,根本就不该偏向。于是整个类的所有对象
都会变为不可偏向的,新建的对象也是不可偏向的

package com.dongguo.lockupgrade;


import org.openjdk.jol.info.ClassLayout;

import java.util.Vector;
import java.util.concurrent.locks.LockSupport;

/**
 * @author Dongguo
 * @date 2021/9/11 0011-18:06
 * @description: -XX:BiasedLockingStartupDelay=0
 */
class Dog {
}

public class RedoLockDemo2 {
    static Thread t1, t2, t3;

    public static void main(String[] args) throws InterruptedException {
        int loopNumber = 39;
        Vector<Dog> list = new Vector<>();
        t1 = new Thread(() -> {
            for (int i = 1; i <= loopNumber; i++) {
                Dog d = new Dog();
                list.add(d);
                synchronized (d) {
                    System.out.println(ClassLayout.parseInstance(d).toPrintable2());
                }
            }

            LockSupport.unpark(t2);
        }, "t1");
        t1.start();
        t2 = new Thread(() -> {
            LockSupport.park();

            for (int i = 1; i <= loopNumber; i++) {
                Dog d = list.get(i);
                System.out.println(i + "加锁前  " + ClassLayout.parseInstance(d).toPrintable2());
                synchronized (d) {
                    System.out.println(i + "加锁中  " + ClassLayout.parseInstance(d).toPrintable2());
                }
                System.out.println(i + " 加锁后 " + ClassLayout.parseInstance(d).toPrintable2());
            }

            LockSupport.unpark(t3);
        }, "t2");
        t2.start();
        t3 = new Thread(() -> {
            LockSupport.park();

            for (int i = 1; i <= loopNumber; i++) {
                Dog d = list.get(i);
                System.out.println(i + "加锁前  " + ClassLayout.parseInstance(d).toPrintable2());
                synchronized (d) {
                    System.out.println(i + "加锁中  " + ClassLayout.parseInstance(d).toPrintable2());
                }
                System.out.println(i + " 加锁后 " + ClassLayout.parseInstance(d).toPrintable2());
            }
           
        }, "t3");
        t3.start();
        t3.join();
		System.out.println(ClassLayout.parseInstance(new Dog()).toPrintable2());
        
    }
}

运行结果

00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
-----------
0加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
0加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
0 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
1加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
1加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
1 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
2加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
2加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
2 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
3加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
3加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
3 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
4加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
4加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
4 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
5加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
5加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
5 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
6加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
6加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
6 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
7加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
7加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
7 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
8加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
8加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
8 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
9加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
9加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
9 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
10加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
10加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
10 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
11加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
11加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
11 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
12加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
12加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
12 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
13加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
13加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
13 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
14加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
14加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
14 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
15加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
15加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
15 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
16加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
16加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
16 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
17加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
17加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
17 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
18加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
18加锁中  00000000 00000000 00000000 00111111 01101101 00101111 11110100 00111000 
18 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
19加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
19加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
19 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
20加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
20加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
20 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
21加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
21加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
21 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
22加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
22加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
22 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
23加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
23加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
23 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
24加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
24加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
24 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
25加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
25加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
25 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
26加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
26加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
26 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
27加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
27加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
27 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
28加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
28加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
28 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
29加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
29加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
29 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
30加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
30加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
30 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
31加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
31加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
31 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
32加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
32加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
32 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
33加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
33加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
33 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
34加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
34加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
34 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
35加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
35加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
35 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
36加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
36加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
36 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
37加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
37加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
37 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
38加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00101000 00000101 
38加锁中  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
38 加锁后 00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
-----------
0加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
0加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
0 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
1加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
1加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
1 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
2加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
2加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
2 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
3加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
3加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
3 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
4加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
4加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
4 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
5加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
5加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
5 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
6加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
6加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
6 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
7加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
7加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
7 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
8加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
8加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
8 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
9加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
9加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
9 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
10加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
10加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
10 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
11加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
11加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
11 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
12加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
12加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
12 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
13加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
13加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
13 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
14加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
14加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
14 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
15加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
15加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
15 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
16加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
16加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
16 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
17加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
17加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
17 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
18加锁前  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
18加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
18 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
19加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
19加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
19 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
20加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
20加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
20 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
21加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
21加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
21 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
22加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
22加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
22 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
23加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
23加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
23 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
24加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
24加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
24 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
25加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
25加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
25 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
26加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
26加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
26 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
27加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
27加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
27 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
28加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
28加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
28 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
29加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
29加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
29 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
30加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
30加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
30 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
31加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
31加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
31 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
32加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
32加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
32 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
33加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
33加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
33 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
34加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
34加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
34 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
35加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
35加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
35 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
36加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
36加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
36 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
37加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
37加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
37 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
38加锁前  00000000 00000000 00000001 10001100 00011001 11111110 00110001 00000101 
38加锁中  00000000 00000000 00000000 00111111 01101101 00111111 11110001 01111000 
38 加锁后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 

前半部分t1、t2和批量重偏向一样

t2前20个对象,并没有触发批量重偏向机制,线程t2执行释放同步锁后,转变为无锁形态 共撤销了偏向锁20次

t2第20~40个对象,触发了批量重偏向机制,对象为偏向锁状态,偏向线程t2,

t3前20个对象,并没有触发批量重偏向机制,线程t3执行释放同步锁后,转变为无锁形态 共撤销了偏向锁20次

intx BiasedLockingBulkRevokeThreshold = 40 默认偏向锁批量撤销阈值

此时一共撤销偏向锁40次,达到批量撤销的阈值

t3第20~40个对象,触发批量撤销机制,此时对象锁膨胀变为轻量级锁。

设置-XX:BiasedLockingStartupDelay=0后,创建的新对象本该为偏向锁状态,

在经历过批量重偏向和批量撤销后,直接在实例化后转为无锁状态。

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001

总结

1、批量重偏向和批量撤销是针对类的优化,和对象无关。

2、偏向锁批量重偏向一次之后不可再次批量重偏向。

3、当某个类已经触发批量撤销机制后,JVM会默认当前类产生了严重的问题,剥夺了该类的新实例对象使用偏向锁的权利

轻量级锁(竞争不激烈)

为什么要引入轻量级锁?

在竞争锁对象的线程不多,而且线程持有锁的时间也不长的情景。

即一个对象虽然有多哥线程访问,但是多个线程的访问是错开的(存在少量竞争会不存在竞争)

轻量级锁的获取

轻量级锁是为了在线程近乎交替执行同步块时提高性能。
主要目的: 在没有多线程竞争的前提下,通过CAS减少重量级锁使用操作系统互斥量产生的性能消耗,说白了先自旋再阻塞。

升级时机: 当关闭偏向锁功能或多线程竞争偏向锁会导致偏向锁升级为轻量级锁

假如线程A已经拿到锁,这时线程B又来抢该对象的锁,由于该对象的锁已经被线程A拿到,当前该锁已是偏向锁了。
而线程B在争抢时发现对象头Mark Word中的线程ID不是线程B自己的线程ID(而是线程A),那线程B就会进行CAS操作希望能获得锁。
此时线程B操作中有两种情况:
如果锁获取成功,直接替换Mark Word中的线程ID为B自己的ID(A → B),重新偏向于其他线程(即将偏向锁交给其他线程,相当于当前线程"被"释放了锁),该锁会保持偏向锁状态,A线程Over,B线程上位;

image-20210909100427298

如果锁获取失败,则偏向锁升级为轻量级锁,此时轻量级锁由原持有偏向锁的线程持有,继续执行其同步代码,而正在竞争的线程B会进入自旋等待获得该轻量级锁。

image-20210909100414892

测试轻量级锁

如果关闭偏向锁,就可以直接进入轻量级锁

-XX:-UseBiasedLocking

image-20210909100905345

package com.dongguo.lockupgrade;

import org.openjdk.jol.info.ClassLayout;

/**
 * @author Dongguo
 * @date 2021/9/8 0008-21:46
 * @description:
 */
public class MyObject {
    public static void main(String[] args) {
        Object object = new Object();

        new Thread(() -> {
            synchronized (object) {
                System.out.println(ClassLayout.parseInstance(object).toPrintable());
            }
        }, "t1").start();
    }
}
运行结果
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           40 ef 6f 0a (01000000 11101111 01101111 00001010) (175107904)
      4     4        (object header)                           6e 00 00 00 (01101110 00000000 00000000 00000000) (110)
      8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

后8位01000000

轻量级锁 最后两位00

流程

image-20210909101032317

轻量级锁什么时候升级为重量级锁?

(在轻量级锁状态下继续锁竞争,没有抢到锁的线程将自旋,即不停地循环判断锁是否能够被成功获取。自旋有限度的(有个计数器记录自旋次数,默认允许循环10次,可以通过虚拟机参数更改)。

java6之前是自旋锁

默认启用,默认情况下自旋的次数是 10 次

-XX:PreBlockSpin=10来修改

或者自旋线程数超过cpu核数一半

Java6之后 是自适应自旋锁

自适应意味着自旋的次数不是固定不变的

根据同一个锁上一次自旋的时间。和拥有锁线程的状态来决定。

长时间的自旋操作是非常消耗资源的,一个线程持有锁,其他线程就只能在原地空耗CPU,执行不了任何有效的任务,这种现象叫做忙等(busy-waiting)。

轻量锁与偏向锁的区别和不同

争夺轻量级锁失败时,线程自旋尝试抢占锁

轻量级锁每次退出同步块都需要释放锁,而偏向锁是在竞争发生时才释放锁

重量级锁

有大量的线程参与锁的竞争,冲突性很高,某个达到最大自旋次数的线程,会将轻量级锁升级为重量级锁。

当后续线程尝试获取锁时,发现被占用的锁是重量级锁,则直接进入阻塞状态(而不是忙等),等待被唤醒。)

image-20210909102231399

重量级锁测试

image-20210909102314684

因为自旋会消耗CPU,为了避免无用的自旋(比如获得锁的线程被阻塞住了),一旦锁升级
成重量级锁,就不会再恢复到轻量级锁状态。当锁处于这个状态下,其他线程试图获取锁时,
都会被阻塞住,当持有锁的线程释放锁之后会唤醒这些线程,被唤醒的线程就会进行新一轮
的夺锁之争。

自旋优化TODO

[是否真的理解了偏向锁、轻量级锁、重量级锁(锁膨胀)、自旋锁、锁消除、锁粗化,知道重偏向吗?](https://www.1314i58.com/article/jvm)

img

能发生偏向锁的也可以发生轻量级锁,能发生轻量级锁的也可以适用重量级锁。当然,**重量级锁发生在能够获取到锁的情况,当不能获取到锁时有时会触发自旋锁。**锁消除和锁粗化也是发生在获取到锁的情况,而且是在同步快执行的过程中,所以是在偏向锁的里面。

重量级锁竞争的时候,还可以使用自旋来进行优化,就是先自旋几次,暂时不进入阻塞,如果当前线程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞

synchronized是非公平锁.当线程在进入尾部队列之前,会尝试着先自旋获取锁,如果获取失败才选择进入尾部队列.

阻塞会发生上下文切换,自旋优化尽量避免上下文切换

image-20210911164115060

image-20210911164129541

自旋会占用 CPU 时间,单核 CPU 自旋就是浪费,多核 CPU 自旋才能发挥优势。
在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自旋甚至不自旋,总之,比较智能。
Java 7 之后不能控制是否开启自旋功能。

各种锁优缺点、synchronized锁升级和实现原理

*注意:锁可以升级不可以降级,但是偏向锁状态可以被重置为无锁状态。

image-20210909102542889

synchronized锁升级过程总结:一句话,就是先自旋,不行再阻塞。
实际上是把之前的悲观锁(重量级锁)变成在一定条件下使用偏向锁以及使用轻量级(自旋锁CAS)的形式

synchronized在修饰方法和代码块在字节码上实现方式有很大差异,但是内部实现还是基于对象头的MarkWord来实现的。
JDK1.6之前synchronized使用的是重量级锁,JDK1.6之后进行了优化,拥有了无锁->偏向锁->轻量级锁->重量级锁的升级过程,而不是无论什么情况都使用重量级锁。

偏向锁:适用于单线程适用的情况,在不存在锁竞争的时候进入同步方法/代码块则使用偏向锁。
轻量级锁:适用于竞争较不激烈的情况(这和乐观锁的使用范围类似), 存在竞争时升级为轻量级锁,轻量级锁采用的是自旋锁,如果同步方法/代码块执行时间很短的话,采用轻量级锁虽然会占用cpu资源但是相对比使用重量级锁还是更高效。
重量级锁:适用于竞争激烈的情况,如果同步方法/代码块执行时间很长,那么使用轻量级锁自旋带来的性能消耗就比使用重量级锁更严重,这时候就需要升级为重量级锁。

preview

JIT编译器对锁的优化

JIT

Just In Time Compiler,一般翻译为即时编译器

锁粗化

按理来说,同步块的作用范围应该尽可能小,但是加锁解锁也需要消耗资源,如果存在一系列的连续加锁解锁操作,可能会导致不必要的性能损耗。
锁粗化就是将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁,避免频繁的加锁解锁操作。

例子1

image-20210904184819425

JVM会检测到这样一连串的操作都对同一个对象加锁(while循环内100次执行append,没有锁粗话就要进行100次加锁/解锁) ,此时JVM就会将加锁的范围粗化到这一连串的操作的外部(比如while循环体外) ,使得这一连串操作只需要加一次锁即可。

而且JIT编译器在编译期对StringBuffer对象进行逃逸分析,如果没有发生逃逸,则会使用栈上分配,分配完成后,继续在调用栈内执行,线程结束后,栈空间被回收,局部变量对象也被回收,这样就不用进行垃圾回收了。可以减少垃圾回收时间和次数。

例子2

package com.dongguo.lockupgrade;

/**
 * @author Dongguo
 * @date 2021/9/9 0009-10:37
 * @description: 锁粗化
 * 假如方法中首尾相接,前后相邻的都是同一个锁对象,那JIT编译器就会把这几个synchronized块合并成一个大块,
 * 加粗加大范围,一次申请锁使用即可,避免次次的申请和释放锁,提升了性能
 */
public class LockBigDemo {
    static Object objectLock = new Object();


    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (objectLock) {
                System.out.println("11111");
            }
            synchronized (objectLock) {
                System.out.println("22222");
            }
            synchronized (objectLock) {
                System.out.println("33333");
            }
        }, "a").start();

        new Thread(() -> {
            synchronized (objectLock) {
                System.out.println("44444");
            }
            synchronized (objectLock) {
                System.out.println("55555");
            }
            synchronized (objectLock) {
                System.out.println("66666");
            }
        }, "b").start();
    }
}
 

锁消除

Java虚拟机通过对运行上下文的扫描,经过逃逸分析,去除不可能存在竞争的锁,通过这种方式消除没有必要的锁,可以节省毫无意义的请求锁时间

例子1

image-20210904184122744

StringBuffer是线程安全的 ,因为它的关键方法都被synchronized修饰过的,但是我们看上面代码,我们会发现,sb这个引用只会在add方法中使用,不可能被其它线程引用(因为是局部变量,栈私有),因此sb是不可能共享的资源,JVM会自动消除StringBuffer对象内部的锁

例子2

package com.dongguo.lockupgrade;

/**
 * @author Dongguo
 * @date 2021/9/9 0009-10:29
 * @description: 锁消除
 * 从JIT角度看相当于无视它,synchronized (o)不存在了,这个锁对象并没有被共用扩散到其它线程使用,
 * 极端的说就是根本没有加这个锁对象的底层机器码,消除了锁的使用
 */

public class LockClearUPDemo {
    static Object objectLock = new Object();//正常的

    public void m1() {
        //锁消除,JIT会无视它,synchronized(对象锁)不存在了。不正常的
        //每个线程调用m1方法都会创建一个新的object   即使用的不是同一把锁,不存在锁竞争
        Object o = new Object();

        synchronized (o) {
            System.out.println("-----hello LockClearUPDemo" + "\t" + o.hashCode() + "\t" + objectLock.hashCode());
        }
    }

    public static void main(String[] args) {
        LockClearUPDemo demo = new LockClearUPDemo();

        for (int i = 1; i <= 10; i++) {
            new Thread(() -> {
                demo.m1();
            }, String.valueOf(i)).start();
        }
    }
}

实质是JIT编译器的同步省略或者叫同步消除

锁降级

一般说是不存在锁降级的

扩展

自旋锁(持有锁的时间较短)

指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

自适应自旋锁

自适应自旋解决的是“锁竞争时间不确定”的问题,可以根据上一次自旋的时间与结果调整下一次自旋的时间

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-09-20 16:05:36  更:2021-09-20 16:06:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/9 1:58:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码