【Synchronized】Java 内置锁的使用及原理、锁升级详解

发布时间 2023-03-24 08:08:24作者: 酷酷-

1  前言

这节我们主要讲解下我们平时使用的 Synchronized,它的使用大家应该并不陌生。我们知道 Java 中的锁分为内置锁和显示锁,Synchronized 就是我们的内置锁,内置可以理解为我们只需要标记即可,加锁释放啥的JVM都帮我们做了,那么我们这节就好好来看看这个关键字。

另外我们的版本是:1.8 哈

2  Synchronized 的使用

我们知道锁对象可以是任意的对象,那么关于 Synchronized 的使用大概分三种情况吧:

  • 作用于非静态方法:也就是没有 static 修饰的方法,这时我们的锁对象就是当前执行该方法的对象;
  • 作用于静态方法:就是 static 修饰的方法,这时我们的锁对象是我们的当前类的 Class 对象;
  • 同步代码块:锁对象就是代码块锁的对象;
public class TestSynchronized {
    private Object obj = new Object();
    // 修饰非 static 方法,锁对象为当前对象即this
    public synchronized void test() {
    }
    // 修饰 static 方法,锁对象为当前类 Class
    public synchronized static void say() {
    }
    // 静态代码块,锁对象为 obj
    public void add(int num) {
        synchronized (obj) {
        }
    }
}

3  锁的分类

上节我们在看对象结构的时候,我们看到了对象头中的 Mark Word 中记录着我们锁的信息,我们来回顾下:

其实分类就是为了提效,也算是一种分治思想吧,跟我们平时写业务代码类似,划分场景提高并发甚至每个场景也涉及到切换跟我们这里的锁升级、撤销差不多,那我们就来详细看下每种锁。

3.1  无锁

无锁也可以理解为一种初始状态,就是当我们对象创建出来,我们对象头中的锁信息。那么关于我们创建对象出来,里边的初始所信息又分两种情况:

  • 默认不更改任何 JVM 配置的情况下:前4秒内,创建出来的为无锁(001),4秒钟以后的为偏向锁(101)也可以称为匿名偏向,因为它是偏向锁只是线程 id 都是0;
  • 修改配置-XX:-UseBiasedLocking:关闭偏向锁,那么创建出来的初始锁都是无锁(001),当第一个线程获取锁的时候,会直接变为轻量级锁;
  • 修改配置 -XX:BiasedLockingStartupDelay:修改延迟时间,那么时间内的对象为无锁(001),时间后的为偏向锁(101);

4秒的来源是因为 JVM 启用偏向锁有 4秒 的延迟启动机制,这个可以通过 -XX:BiasedLockingStartupDelay 进行更改,比如 -XX:BiasedLockingStartupDelay=0 即延迟0秒。

我们来运行下看看是不是:

1.8版本哈(1.6以后偏向锁默认开启),我们在不更改任何配置的情况下:

当我们