并发编程之 Synchronized 关键字底层原理与锁升级

目录

并发编程之 Synchronized 关键字底层原理与锁升级

一、synchronized关键字与monitor

二、锁升级

(一)偏向锁

(二)轻量级锁

(三)重量级锁

三、Java 对象的内存结构与锁的关联

(一)对象头

(二)实例数据

(三)对齐填充数据


在并发编程中,synchronized关键字是实现线程安全的重要手段,其底层原理涉及到多种锁机制以及锁升级的概念。

一、synchronized关键字与monitor

Synchronized关键字在底层是通过monitor实现的。monitor是 JVM 提供的、用 C++ 实现的一种机制。当线程使用synchronized关键字时,线程需要关联monitor,这个过程会涉及到用户态和内核态的切换。用户态指的是我们编写的 Java 代码执行的状态,内核态则是 CPU 层面的状态,二者资源权限不同,用户态权限低,内核态权限高。由于 JVM 是系统级别的,属于内核态,而线程受 JVM 管理,这种切换成本较高,并且存在进程上下文切换,导致monitor这种锁机制的性能相对较低,所以monitor实现的锁被称为重量级锁。

二、锁升级

在 JDK1.6 之后,引入了偏向锁和轻量级锁,这是为了在不同的场景下提高性能。

(一)偏向锁

当一个线程获取锁,并且在很长一段时间内,只有这一个线程重复获取该锁(没有其他线程竞争)时,使用偏向锁可以提高性能。例如:

class MyClass {
    private Object lock = new Object();
    public void method1() {
        synchronized(lock) {
            // 这里是同步代码块
        }
    }
    public void method2() {
        synchronized(lock) {
            // 这里是同步代码块,和 method1 使用同一个锁对象
        }
    }
}

在上述代码中,如果一个线程先调用method1,然后调用method2,都是对同一个lock对象加锁,这种情况下就适合使用偏向锁。

偏向锁在对象头的mark word中有特殊的记录方式。在 32 位虚拟机中,mark word里有线程 ID(23 位)、偏向锁的时间戳(2 位),偏向锁标识(1 位),当标识为 1 且后三位是 101 时,表示是偏向锁。当一个线程第一次获取锁时,会通过 CAS 操作将自己的线程 ID 等信息写入mark word,之后该线程再次获取锁时,只需判断mark word中的线程 ID 是否是自己,不需要再进行 CAS 操作。

(二)轻量级锁

当多个线程获取锁,但获取锁的时候没有竞争,或者是同步代码块中的代码不存在竞争,线程是交替执行的情况,使用轻量级锁更合适。例如:

class MyClass {
    Object obj = new Object();
    public void method1() {
        synchronized(obj) {
            // 同步代码块内容
        }
    }
    public void method2() {
        synchronized(obj) {
            // 同步代码块内容,和 method1 使用同一个对象锁
        }
    }
}

当一个线程执行method1时,会创建一个锁记录(local record),这个锁记录包含指向锁对象的指针、锁记录地址等信息。然后通过 CAS 操作将对象的mark word数据与锁记录的数据进行交换,如果交换成功,对象头就存储锁记录地址和表示该线程拥有锁的状态(00)。如果 CAS 操作失败,可能是有多个线程竞争,此时会升级为重量级锁;也可能是锁重入,会在栈帧中再添加一条local record作为重入计数。当线程退出同步代码块时,如果锁记录不为空,会通过 CAS 操作将数据交换回来完成解锁。

(三)重量级锁

当锁发生竞争时,就会使用重量级锁(monitor)来解决。在重量级锁中,对象锁是通过在对象头的mark word中记录monitor的地址,从而与monitor关联起来。例如,在 32 位虚拟机中,mark word的最后两位是 10 时,表示是重量级锁,前面的指针指向monitor系统。

三、Java 对象的内存结构与锁的关联

在 JVM 的内存结构中,创建的对象都在堆中。以 HotSpot 虚拟机为例,对象分为三个部分:对象头、实例数据和对齐填充数据。

(一)对象头

对象头又分为mark wordclass wordmark word与锁机制密切相关,它记录了对象与monitor的关联方式、锁的状态等信息。class word描述的是对象实例的具体类型。

(二)实例数据

就是类的成员变量,比如User类中有nameage等成员变量,实例数据存储的就是这些成员变量的数据。

(三)对齐填充数据

如果对象头和实例变量占用的内存不是 8 的整数倍,则需要一些无意义的数据进行填充,使内存起始地址为 8 的倍数,这是 HotSpot 虚拟机内存管理系统的要求。

通过理解synchronized关键字底层的锁机制以及 Java 对象内存结构与锁的关联,可以更好地掌握并发编程中的线程安全问题,从而编写出更高效、稳定的多线程代码。无论是后端的 Java 代码还是前端的 Vue 代码,在涉及到多线程相关的功能实现时,这些知识都非常关键,而 Python 代码在其他相关并发场景中也可以借鉴这些原理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值