synchronized的底层原理

news/2024/9/22 20:50:20/

目录

介绍

实现原理

对象头

Monitor(监视器)

锁升级

偏向锁

轻量级锁

重量级锁

锁的优缺点


介绍

synchronized 是 Java 中的关键字,它用于锁定代码块或方法,以确保同一时刻只有一个线程可以进入被锁定的部分。这在多线程编程中非常重要,因为它有助于避免多个线程同时访问共享资源时引发的数据不一致问题。synchronized 可以锁定对象,也可以锁定类。锁的对象决定了哪些线程可以进入锁定区域。

先来看下利用synchronized实现同步的基础:Java中的每一个对象都可以作为锁。具体表现
为以下3种形式。

  • 对于普通同步方法,锁是当前实例对象。
  • 对于静态同步方法,锁是当前类的Class对象。
  • 对于同步方法块,锁是Synchonized括号里配置的对象。

实现原理

Synchronized的底层实现是完全依赖JVM虚拟机的,因此synchronized的底层原理一定会涉及到JVM对象存储中的对象头和Monitor(监视器),想要了解底层原理需要从这两方面入手。

对象头

真正标志某个对象是否上synchronized锁的位置就是对象头。

对象的头信息中存放着类信息和锁信息,如果数组的话还会存放着长度

对象头可以分为三部分,锁信息在第一个部分Mark Word。32位操作系统前两部分是32bit,如果是64位操作系统前两部分就是641bit。

锁相当于一个标记,一个俗称标记

Java对象头里的Mark Word里默认存储对象的HashCode、分代年龄和锁标记位。下图位32位虚拟机对象头中MarkWord中的信息。

如果一个线程对对象头部修改成功加锁了,其他线程就不能再加锁了

Monitor(监视器)

jdk1.6之前,synchronized只能实现重量级锁,Java虚拟机是基于Monitor对象来实现重量级锁的。

随着Java SE 1.6对synchronized进行了各种优化之后,有些情况下它就并不那么重了。Java SE 1.6中为了减少获得锁和释放锁带来的性能消耗而引入了偏向锁和轻量级锁,以及锁的存储结构和升级过程。

什么是Monitor?

每个 Java 对象都关联一个 monitor,也称为监视器或管程,用于实现对象级别的锁定。

当一个线程进入一个 synchronized 代码块或方法时,它会尝试获取该对象的 monitor。如果该 monitor 已被其他线程占用,则当前线程会被阻塞,直到获取到该 monitor。

如何使用?

在JVM中是基于进入和退出Monitor对象来实现方法同步和代码块同步,但两者的实现细节不一样。代码块同步是使用monitorenter和monitorexit指令实现的,而方法同步是使用另外一种方式实现,同时也可以使用这两个指令来实现。

monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。

锁升级

上面提到了1.6中为了减少获得锁和释放锁带来的性能消耗而引入了偏向锁和轻量级锁,因此锁就有了一个升级的过程。

首先上了锁之后,会先上偏向锁,偏向锁升级后会变为轻量级锁,轻量级锁升级后会变为重量级锁。

偏向锁

大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。

当一个线程访问同步块并获取锁时,会在对象头和栈帧(栈帧指的就是方法)中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。

如果测试成功,表示线程已经获得了锁。如果测试失败,则需要再测试一下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁):如果没有设置,则使用CAS竞争锁;如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。

这里需要注意:

因为头部锁信息占8字节,总线不够用,不能一次全部更改完,一个线程在更新时要改两次,因此加锁时要用CAS进行比较,比较时符合再操作,两次修改时都要比较,以防止第一次改完后别的线程再进行更改,只要是刚往回更新就不允许别的线程修改了。

偏向锁的特性:偏向锁在线程执行完依然不解锁,下次再执行时也无需再上锁

轻量级锁

什么是轻量级锁:

一旦有其他线程跟加偏向锁的线程竞争,偏向锁就会自动升级为轻量级锁,竞争失败的线程会进入就绪队列,并一直死循环进行探测,进行CAS看能否进行替换(这个过程叫做自旋,也就是死循环),一旦上锁的线程执行完,直接给就绪队列中的线程加上锁。(适用于线程竞争小,每个线程的执行时间较短的情况)

轻量级锁加锁:

线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方称为Displaced Mark Word。

然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

轻量级锁解锁:

轻量级解锁时,会使用原子的CAS操作将Displaced Mark Word替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。

轻量级锁的特点:一旦其他线程释放锁,它能以最快的速度感知到,并加上锁

重量级锁

当有很多进程竞争,并且正在运行的加锁线程需要运行很长时间时,失败后进行自旋时,会造成性能的浪费,此时升级为重量级锁,让失败的线程在进行一次CAS比较失败后就进入阻塞队列,以减少性能的浪费。

锁的升级过程不可逆,一旦成了重量级锁就不能变为偏向锁和轻量级锁了

锁的优缺点


http://www.ppmy.cn/news/1435001.html

相关文章

数据库——实 验 8 SQL 编程

1.T-SQL 语言简介 SQL Server 使用的语言称作 Transact-SQL, 它不仅包括基本 SQL 操作的内容,如 SQL 的数据查询功能和数据操作功能等,还有一般程序设计的能力。 2. 局部变量和全局变量的概念 1)局部变量 局部变量是一个能够拥有特定数据类型的对…

01-服务与服务间的通信

这里是极简版,仅用作记录 概述 前端和后端可以使用axios等进行http请求 服务和服务之间也是可以进行http请求的spring封装的RestTemplate可以进行请求 用法 使用bean注解进行依赖注入 在需要的地方,自动注入RestTemplate进行服务和服务之间的通信 注…

SQL-DML数据操纵语言(Oracle)

文章目录 DML数据操纵语言常见的字段属性字符型字段属性char(n)varchar2(n)/varchar(n) 数值型字段属性number([p],[s]int 日期型字段属性DATEtimestamp 如何查看字段属性增加数据INSERT快捷插入 删除数据DELETE修改数据UPDATE DML数据操纵语言 定义 是针对数据做处理&#xf…

8.4.1 实验1:创建 VLAN 和划分端口

1、实验目的 通过本实验可以掌握: VLAN的概念。创建VLAN的方法。把交换机端口划分到VLAN中的方法。 2、实验拓扑 创建 VLAN 和划分端口的实验拓扑如下图所示。 图8-5 创建 VLAN 和划分端口的实验拓扑 3、实验步骤 (1)实验准备 S1#eras…

vue 实现级联选择器功能

vue开发中&#xff0c;通过使用 Element UI 的 el-cascader 组件来实现级联选择器功能,下面是一个示例代码&#xff0c;演示如何使用 el-cascader 组件初始化级联选择器&#xff0c;并设置默认值为单位 测试1 和部门 测试11 <template><div><el-cascaderv-mode…

系统思考—啤酒游戏

最近有不少的合作伙伴来询问我啤酒游戏这个来自于MIT&#xff08;麻省理工学院&#xff09;经典的沙盘&#xff0c;上周刚刚结束Midea旗下的一家公司市场运营部《啤酒游戏沙盘-应对动态性复杂的系统思考智慧》的课程。 参与这次沙盘体验的团队成员深刻体会到了全局思考的重要性…

Maxwell安装使用和简单案例

一、解压 cd /opt/software/ ​ tar -zxvf maxwell-1.29.2.tar.gz -C /opt/module/ ​ cd /opt/module/ 二、MySQL 环境准备 1、修改 mysql 的配置文件 修改 mysql 的配置文件&#xff0c;开启 MySQL Binlog 设置 vi /etc/my.cnf 添加以下内容 server_id1 log-binmysql-…

09 JavaScript学习:对象

对象的概念 在计算机科学中&#xff0c;对象是一种数据结构&#xff0c;用于存储数据和方法&#xff08;也称为函数&#xff09;。对象可以包含属性&#xff08;也称为成员变量&#xff09;和方法&#xff08;也称为成员函数&#xff09;&#xff0c;通过这些属性和方法可以描述…