当前位置: 首页 > news >正文

Java并发之synchronized详解

☆* o(≧▽≦)o *☆嗨~我是小奥🍹
📄📄📄个人博客:小奥的博客
📄📄📄CSDN:个人CSDN
📙📙📙Github:传送门
📅📅📅面经分享(牛客主页):传送门
🍹文章作者技术和水平有限,如果文中出现错误,希望大家多多指正!
📜 如果觉得内容还不错,欢迎点赞收藏关注哟! ❤️

文章目录

  • Java并发之synchronized详解
  • 一、synchronized
  • 二、synchronized原理
    • (1)对象头
    • (2)Monitor
    • (3)工作流程
  • 三、synchronized优化
    • (1)锁升级
      • ① 偏向锁
      • ② 轻量级锁
      • ③ 锁膨胀
      • ④ 重量级锁
    • (2) 锁优化
      • ① 自旋锁
      • ② 锁消除
      • ③ 锁粗化

Java并发之synchronized详解

一、synchronized

synchronized是Java中的关键字,是一种同步锁,他修饰的对象有以下几种:

  • 修饰一个代码块,被修饰的代码块称为同步方法块,其作用的范围是使用大括号括起来的代码,作用的对象是调用这个代码块的对象。
  • 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象。
  • 修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象。
  • 修饰一个类,其作用的范围是synchronized后面括起来的部分,作用的对象是这个类的所有对象。

无论synchronized关键字加在方法上还是对象上,

  • 如果它作用的对象是非静态的,则它获取的是对象;
  • 如果它作用的对象是一个静态方法或一个类,则它获取的锁是对于类的,该类的所有对象持有同一把锁。

实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免不必要的同步控制。

二、synchronized原理

(1)对象头

在JVM中,对象在内存中存储的布局可以分为三个区域,分别是对象头示例数据以及填充数据

  • 对象头:在HotSpot虚拟机中,对象头被分为两部分,分别为:Mark Word(标记字段)、Class Pointer(类型指针),如果是数组,那么还会有数组长度
  • 实例数据存储类的属性数据信息,包括父类的属性信息,这部分内存按4字节填充。
  • 填充数据:由于虚拟机要求对象起始地址必须是8字节的整数倍,所以填充数据不是必要存在的,仅仅是为了字节对齐。

而Java对象头则是实现synchronized的锁对象的基础。

(2)Monitor

synchronized 用的锁是存在 Java 对象头里的。

  • 如果对象是数组类型,则虚拟机用 3 个字宽(Word)存储对象头;
  • 如果对象是非数组类型,则用 2 字宽存储对象头。

在 32 位虚拟机中,1 字宽等于 4 字节,即 32bit.

在这里插入图片描述

Monitor的结构

Java 对象头里的 Mark Word 里默认存储对象的 HashCode、分代年龄和锁标记位。另外,数组还会有长度的标识。

在运行期间,Mark Word里存储的数据会随着锁的标志位的变化而变化。

在这里插入图片描述

(3)工作流程

1、开始时Monitor中的owner为null

2、当线程1执行synchronized(obj)时就会将Monitor的所有者owner置为线程1,Monitor中只能有1个Owner,obj对象中的Mark Word指向Monitor,把对象原有的Mark Word存储线程栈中的锁记录中。

3、线程1上锁的过程中,如果有其他线程来执行synchronized(obj),就会进入EntryList BLOCKED

4、线程1执行完同步代码块的内容,根据 obj 对象头中 Monitor 地址寻找,设置 Owner 为空,把线程栈的锁记录中的对象头的值设置回 MarkWord

5、唤醒 EntryList 中等待的线程来竞争锁,竞争是非公平的,如果这时有新的线程想要获取锁,可能直接就抢占到了,阻塞队列的线程就会继续阻塞。

(4)字节码层面分析

public static void main(String[] args) {Object lock = new Object();synchronized (lock) {System.out.println("ok");}
}
 0 new #2 <java/lang/Object>	// new Object3 dup4 invokespecial #1 <java/lang/Object.<init> : ()V>		// invokespecial <init>:()V,非虚方法7 astore_1						// lock引用 -> lock8 aload_1						// lock (synchronized开始)9 dup							// 一份用来初始化,一份用来引用
10 astore_2						// lock引用 -> slot 2
11 monitorenter					// 【将 lock对象 MarkWord 置为 Monitor 指针】
12 getstatic #3 <java/lang/System.out : Ljava/io/PrintStream;>
15 ldc #4 <ok>
17 invokevirtual #5 <java/io/PrintStream.println : (Ljava/lang/String;)V>
20 aload_2						// slot 2(lock引用)
21 monitorexit					// 【将 lock对象 MarkWord 重置, 唤醒 EntryList】
22 goto 30 (+8)
25 astore_3						// any -> slot 3
26 aload_2						// slot 2(lock引用)
27 monitorexit					// 【将 lock对象 MarkWord 重置, 唤醒 EntryList】
28 aload_3
29 athrow
30 return

实现原理:同步代码块通过moniterentermoniterexit关联到一个monitor对象,进入时设置Owner为当前线程,计数+1,退出-1,除了正常入口的moniterenter,还在异常入口的地方加入了moniterexit指令。

三、synchronized优化

(1)锁升级

synchronized 是可重入、不公平的重量级锁,所以可以对其进行优化。

在 Java SE 1.6 中,锁一共有 4 种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态重量级锁状态,这几个状态会随着竞争情况逐渐升级。

锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。

无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁	// 随着竞争的增加,只能锁升级,不能降级

在这里插入图片描述

① 偏向锁

解决问题:大多数情况下,**线程不仅不存在多线程竞争,而且锁总是由同一线程多次获得,在没有其他线程竞争锁时,线程每次重入锁仍然需要进行CAS操作,造成性能的损耗。**为了让多线程获得锁的代价更低而引入了偏向锁。

偏向锁加锁:

偏向锁的思想是偏向于让第一个获取锁对象的线程,这个线程之后重新获取该锁不再需要同步操作

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

偏向锁撤销:

偏向锁使用了一种等到竞争出现才释放锁的机制,所以当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁

偏向锁的撤销,需要等待全局安全点(在这个时间点上没有正在执行的字节码)。

  • 它会首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着,如果线程不处于活动状态,则将对象头设置成无锁状态;
  • 如果线程仍然活着,拥有偏向锁的栈会被执行,遍历偏向对象的锁记录,栈中的锁记录和对象头的Mark Word 要么重新偏向于其他线程,要么恢复到无锁或者标记对象不适合作为偏向锁,最后唤醒暂停的线程,从安全点继续执行代码。

偏向锁关闭:

// 偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加 VM 参数
-XX:BiasedLockingStartupDelay=0 //  关闭偏向锁的延迟
-XX:-UseBiasedLoacking=false //  关闭偏向锁

JDK8延迟4每秒开启偏向锁的原因:当程序刚开始执行时,会有很多的线程来争抢锁,如果开启偏向锁效率反而会降低。

撤销偏向锁的场景

  • 调用对象的hashCode:偏向锁的对象MarkWord中存储的是线程的id,调用hashCode导致偏向锁被撤销;
  • 当有其他线程来竞争锁的时候,会将偏向锁升级为轻量锁;
  • 调用wait/notify,需要申请Monitor,进入WaitSet。

批量撤销和批量重偏向:

从偏向锁的加锁解锁过程中就可以看出,当只有一个线程反复获取锁的是皇后,偏向锁带来的性能开销可以忽略,但是当有其他线程尝试获得锁时,就需要等到safe point时,再将偏向锁撤销为无锁状态或升级为轻量级,会消耗一定的性能,所以在多线程竞争频繁的情况下,偏向锁不仅不能提高性能,还会导致性能下降。

批量撤销(解决场景):在明显多线程竞争剧烈的场景下使用偏向锁是不合适的。

批量重偏向(解决场景):一个线程创建了大量对象并执行了初始的同步操作,后来另一个线程也来将这些对象作为锁对象进行操作,这样会导致大量的偏向锁撤销操作。

原理:以class为单位,为每个class维护一个偏向锁撤销计数器,每一次该class的对象发生偏向撤销操作时,该计数器+1,当这个值达到重偏向阈值(默认20)时,JVM就认为该class的偏向锁有问题,因此会进行批量重偏向。每个class对象会有一个对应的epoch字段,每个处于偏向锁状态对象的Mark Word中也有该字段,其初始值为创建该对象时class中的epoch的值。

每次发生批量重偏向时,就将该值+1,同时遍历JVM中所有线程的栈,找到该class所有正处于加锁状态的偏向锁,将其epoch字段改为新值。下次获得锁时,发现当前对象的epoch值和class的epoch不相等,那就算当前已经偏向了其他线程,也不会执行撤销操作,而是直接通过CAS操作将其Mark Word的Thread Id 改成当前线程Id。
当达到重偏向阈值后,假设该class计数器继续增长,当其达到批量撤销的阈值后(默认40),JVM就认为该class的使用场景存在多线程竞争,会标记该class为不可偏向,之后,对于该class的锁,直接走轻量级锁的逻辑。

② 轻量级锁

加锁:线程在执行同步块之前,JVM 会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的 Mark Word 复制到锁记录中,官方称为 Displaced Mark Word。然后线程尝试使用 CAS 将对象头中的 Mark Word 替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

  • 创建锁记录对象(Lock Record)对象,每个线程的栈帧都会包含一个锁记录的结构,存储锁定对象的 Mark Word
  • 让锁记录中 Object reference 指向锁住的对象,并尝试用 CAS 替换 Object 的 Mark Word,将 Mark Word 的值存入锁记录
  • 如果 CAS 替换成功,对象头中存储了锁记录地址和状态 00(轻量级锁) ,表示由该线程给对象加锁
  • 如果 CAS 失败,有两种情况:
    • ​ 如果是其它线程已经持有了该 Object 的轻量级锁,这时表明有竞争,进入锁膨胀过程
    • 如果是线程自己执行了 synchronized 锁重入,就添加一条 Lock Record 作为重入的计数

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

  • 当退出 synchronized 代码块(解锁时)
    • 如果有取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重入计数减 1
    • 如果锁记录的值不为 null,这时使用 CAS 将 Mark Word 的值恢复给对象头
      • 成功,则解锁成功
      • 失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程

③ 锁膨胀

在尝试加轻量级锁的过程中,CAS 操作无法成功,可能是其它线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁

  • 当 Thread-1 进行轻量级加锁时,Thread-0 已经对该对象加了轻量级锁
  • Thread-1 加轻量级锁失败,进入锁膨胀流程:为 Object 对象申请 Monitor 锁,通过 Object 对象头获取到持锁线程,将 Monitor 的 Owner 置为 Thread-0,将 Object 的对象头指向重量级锁地址,然后自己进入 Monitor 的 EntryList BLOCKED
  • 当 Thread-0 退出同步块解锁时,使用 CAS 将 Mark Word 的值恢复给对象头失败,这时进入重量级解锁流程,即按照 Monitor 地址找到 Monitor 对象,设置 Owner 为 null,唤醒 EntryListBLOCKED 线程。

在这里插入图片描述

④ 重量级锁

重量级锁是指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。

重量级锁通过对象内部的监视器(monitor)实现,而其中 monitor 的本质是依赖于底层操作系统的 Mutex Lock 实现,操作系统实现线程之间的切换需要从用户态切换到内核态,切换成本非常高。

(2) 锁优化

① 自旋锁

当一个线程尝试获取锁(重量级锁)时,如果锁已经被其他线程持有,该线程会进入自旋状态,不断地重试获取锁,直到获取到为止。自旋锁避免了线程切换带来的开销,但是如果锁被持有的时间较长,自旋锁可能会导致CPU资源的浪费。

② 锁消除

锁消除是一种编译器优化技术,编译器可以在编译过程中分析代码,并根据程序的特性来消除一些不必要的锁操作

比如在单线程程序中使用锁,锁会变成多余的开销,编译器可以消除这些锁操作,从而提高程序的性能。

锁消除主要是通过逃逸分析来支持,如果堆上的共享数据不可能逃逸出去被其它线程访问到,那么就可以把它们当成私有数据对待,也就可以将它们的锁进行消除(同步消除:JVM 逃逸分析)

③ 锁粗化

锁粗化是一种优化技术,可以将多个连续的锁操作合并成一个锁操作。例如,如果程序中存在多个连续的对同一个锁的加锁和解锁操作,锁粗化可以将它们合并为一个锁操作,从而减少锁的竞争和开销,提高程序的性能。

相关文章:

Java并发之synchronized详解

☆* o(≧▽≦)o *☆嗨~我是小奥&#x1f379; &#x1f4c4;&#x1f4c4;&#x1f4c4;个人博客&#xff1a;小奥的博客 &#x1f4c4;&#x1f4c4;&#x1f4c4;CSDN&#xff1a;个人CSDN &#x1f4d9;&#x1f4d9;&#x1f4d9;Github&#xff1a;传送门 &#x1f4c5;&a…...

Flask 项目自动生成 API 文档的高效实践

Flasgger&#xff0c;作为一款强大的 Flask 扩展&#xff0c;自动从 Flask 应用中提取并生成 OpenAPI 规范文档&#xff0c;配备 SwaggerUI&#xff0c;为开发者提供了一条快捷通道&#xff0c;让 API 的文档编制和交互式测试变得简单易行。Flasgger 的设计原则是简化开发流程&…...

WebChat——一个开源的聊天应用

Web Chat 是开源的聊天系统&#xff0c;支持一键免费部署私人Chat网页的应用程序。 开源地址&#xff1a;https://github.com/loks666/webchat 目录树 TOC &#x1f44b;&#x1f3fb; 开始使用 & 交流&#x1f6f3; 开箱即用 A 使用 Docker 部署B 使用 Docker-compose…...

【Linux系统 01】Vim工具

目录 一、Vim概述 1. 文件打开方式 2. 模式切换 二、命令模式 1. 移动与跳转 2. 复制与粘贴 3. 剪切与撤销 三、编辑模式 1. 插入 2. 替换 四、末行模式 1. 保存与退出 2. 查找与替换 3. 分屏显示 4. 命令执行 一、Vim概述 1. 文件打开方式 vim 文件路径&#…...

Oracle 面试题 | 09.精选Oracle高频面试题

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

基于Springboot的校园失物招领网站(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的校园失物招领网站&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…...

WPF布局面板

StackPanel StackPanel 是一种常用的布局控件,可以支持水平或垂直排列,但不会换行。当子元素添加到 StackPanel 中时,它们将按照添加的顺序依次排列。默认情况下,StackPanel 的排列方向是垂直的,即子元素将从上到下依次排列。可以使用 Orientation 属性更改排列方向。可以…...

灵活应对:策略模式在软件设计中的应用

策略模式是一种行为型设计模式&#xff0c;它允许定义一系列算法&#xff0c;并将每个算法封装起来&#xff0c;使它们可以互换使用。策略模式让算法的变化独立于使用算法的客户端&#xff0c;使得在不修改原有代码的情况下切换或扩展新的算法成为可能。 使用策略模式的场景包…...

eosio.token 智能合约介绍

一、目的 eosio.token系统合约定义了允许用户为基于EOSIO的区块链创建、发行和管理代币的结构和操作&#xff0c;它演示了一种实现允许创建和管理代币的智能合约的方法。本文详细介绍了eosio.token系统合约并在本地测试链上实际发行了代币进行演示&#xff0c;适用于EOS智能合…...

3D 转换

1&#xff0c;3D的特点&#xff1a; 近小远大 物体后面遮挡不可见 2&#xff0c;3D移动 translate3d 3D移动在2D移动的基础上多加了一个可以移动的方向&#xff0c;就是z轴方向 transform&#xff1a;translateX&#xff08;100px&#xff09;&#xff1a;仅仅是在x轴上移动…...

AI智能语音机器人安装方法

销售型的企业&#xff0c;基本靠电话营销拓客来实现效益的最大化。因为电销人员离职率高&#xff0c;且需求量大&#xff0c;需要接连不断的招人来实现业绩目标&#xff0c;电话机器人,贴牌招商,复制多个账户 这些都意味的企业的投入成本越来越大&#xff0c;博主从事多年AI技术…...

Python 潮流周刊#38:Django + Next.js 构建全栈项目

△△请给“Python猫”加星标 &#xff0c;以免错过文章推送 你好&#xff0c;我是猫哥。这里每周分享优质的 Python、AI 及通用技术内容&#xff0c;大部分为英文。本周刊开源&#xff0c;欢迎投稿[1]。另有电报频道[2]作为副刊&#xff0c;补充发布更加丰富的资讯&#xff0c;…...

Jenkins升级后,构建任务配置界面重复错位

最近我把公司的Jenkins服务升级到了最新版本&#xff0c;升级完成后&#xff0c;点了一下构建任务&#xff0c;发现能够构建成功&#xff0c;就以为顺利完成升级了&#xff0c;下班走了&#xff0c;结果第二天&#xff0c;进入构建任务配置界面发现&#xff0c;界面一团乱麻&am…...

Python基础学习 -07 运算符

Python 运算符 运算符用于对变量和值执行操作。 Python 在以下组中划分运算符&#xff1a; 算术运算符赋值运算符比较运算符逻辑运算符身份运算符成员运算符位运算符 Python 算术运算符 算术运算符与数值一起使用来执行常见的数学运算&#xff1a; 运算符名称实例加x y-…...

Nim游戏

文章目录 题目描述输入格式输出格式 结论程序代码 题目描述 给定 n 堆石子&#xff0c;两位玩家轮流操作&#xff0c;每次操作可以从任意一堆石子中拿走任意数量的石子&#xff08;可以拿完&#xff0c;但不能不拿&#xff09;&#xff0c;最后无法进行操作的人视为失败。 问…...

Pytorch: torch.linspace等间隔数值函数

torch.linspace 是 PyTorch 提供的一个用于生成等间隔数值的函数。具体而言&#xff0c;torch.linspace 会在指定的区间内生成指定数量的等间隔数值。 torch.linspace(start, end, steps100, dtypeNone, layouttorch.strided, deviceNone, requires_gradFalse)参数说明&#x…...

【C++】案例:数列求和 与 条件筛选

1.数列求和 题目&#xff1a; 设计一个程序&#xff0c;要求对数列2471116……n的前n项求和&#xff0c; 例如输入3&#xff0c;输出13; 输入6&#xff0c;输出62。 答案&#xff1a; #include <iostream>int main() {int n;std::cout << "请输入一个正…...

问题:下列哪些属于历史文化资源的特征( ). #学习方法#学习方法

问题&#xff1a;下列哪些属于历史文化资源的特征( ). A、稀缺性 B、脆弱性 C、可再生性 D、多样性 参考答案如图所示...

大数据 - Spark系列《四》- Spark分布式运行原理

Spark系列文章&#xff1a; 大数据 - Spark系列《一》- 从Hadoop到Spark&#xff1a;大数据计算引擎的演进-CSDN博客 大数据 - Spark系列《二》- 关于Spark在Idea中的一些常用配置-CSDN博客 大数据 - Spark系列《三》- 加载各种数据源创建RDD-CSDN博客 目录 &#x1f360;…...

Java使用规范

1.关键字 定义&#xff1a;被Java语言赋予了特殊含义&#xff0c;用做专门用途的字符串(单词) 特点&#xff1a;关键字中的所有字母都是小写 2.保留字 java保留字&#xff1a;现有Java版本尚未使用&#xff0c;但以后的版本可能会作为关键字使用。命名标识符时要避免使用这些…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

PH热榜 | 2025-06-08

1. Thiings 标语&#xff1a;一套超过1900个免费AI生成的3D图标集合 介绍&#xff1a;Thiings是一个不断扩展的免费AI生成3D图标库&#xff0c;目前已有超过1900个图标。你可以按照主题浏览&#xff0c;生成自己的图标&#xff0c;或者下载整个图标集。所有图标都可以在个人或…...