volatile,synchronized,原子操作实现原理,缓存一致性协议

article/2025/6/30 18:09:17

文章目录

  • 缓存一致性协议(MESI)
  • volatile
    • 1. volatile 的作用
    • 2.volatile的底层实现
    • 3,volatile 实现单例模式的双重锁(面手写)
  • synchronized
    • 1,基本用法
    • 2,可重入性
    • 3,Java对象头
    • 4,实现原理
      • (1)代码块同步的实现
      • (2)方法同步的实现
    • 5,锁的升级与对比
  • 原子操作的实现原理
    • 1,术语
    • 2,如何实现原子操作
    • 3,Java如何实现原子操作
        • CAS实现原子操作的三大问题

  • CAS及其损耗CPU性能

缓存一致性协议(MESI)

MESI 是四种缓存行状态的缩写:

状态英文全称说明
M (Modified)已修改缓存行已被当前CPU修改,与主存不一致,其他CPU不能持有该数据的有效副本
E (Exclusive)独占缓存行仅被当前CPU持有,与主存一致,其他CPU没有该数据的副本
S (Shared)共享缓存行被多个CPU共享,所有副本与主存一致
I (Invalid)无效缓存行数据已失效,必须从主存或其他CPU重新加载

MESI 的工作示例:

假设两个CPU核心(Core1和Core2)访问同一内存地址 X

  1. 初始状态
    • X 在主存中的值为 0
    • Core1和Core2的缓存中均无 X
  2. Core1 读取 X
    • Core1 缓存 X,状态变为 E (Exclusive)
    • 直接从主存加载 X=0
  3. Core2 读取 X
    • Core1 的 X 状态降级为 S (Shared)
    • Core2 也缓存 X,状态为 S
  4. Core1 修改 X=1
    • Core1 发送 总线事务,使 Core2 的 X 缓存行失效(状态变为 I
    • Core1 的 X 状态变为 M (Modified),并更新缓存值
  5. Core2 再次读取 X
    • 发现 X 缓存行无效(状态为 I
    • 向总线发送请求,Core1 将 X=1 写回主存,并降级为 S
    • Core2 重新加载 X=1,状态变为 S

volatile

volatile 是 Java 提供的一种轻量级同步机制,用于确保多线程环境下的 可见性禁止指令重排序,但它 不保证原子性

特性说明实现原理
可见性一个线程修改 volatile 变量后,其他线程立即可见新值内存屏障 + 缓存一致性协议(MESI)
有序性禁止 JVM 对 volatile 变量的读写操作进行重排序插入内存屏障指令
非原子性volatile 不能保证复合操作(如 i++)的原子性需配合 synchronized/CAS

1. volatile 的作用

(1) 保证可见性

  • 问题:普通变量在多线程环境下,一个线程修改后,其他线程可能无法立即看到最新值(由于 CPU 缓存)。
  • volatile 的解决方案
    • 写操作:立即刷新到主内存,并 使其他 CPU 缓存失效
    • 读操作:强制从主内存重新加载最新值。

(2) 禁止指令重排序

  • 问题:JVM 和 CPU 可能对指令进行优化重排,导致多线程环境下出现意外行为。
  • volatile 的解决方案
    • 通过 内存屏障(Memory Barrier) 禁止 JVM 和 CPU 对 volatile 变量的读写操作进行重排序。

2.volatile的底层实现

  1. 内存屏障

    • 写操作

      • **StoreStore 屏障:**位于volatile之前,确保 volatile 写之前的 所有普通写操作 都已完成(刷新到主内存)
      • **StoreLoad 屏障:**位于volatile之后,禁止当前 Store 与之后的 Load 重排序;强制刷新写缓冲区到主内存。
      // 线程1
      x = 1;                // 普通写
      StoreStoreBarrier();  // 确保 x=1 刷入内存
      volatileVar = 2;      // volatile 写
      StoreLoadBarrier();   // 确保 volatile 写对所有线程可见
      
    • 读操作

      • **LoadLoad 屏障:**位于volatile之后,防止 volatile 读与 后续的普通读操作 重排序
      • **LoadStore 屏障:**位于volatile之后,防止 volatile 读与 后续的普通写操作 重排序
      // 线程2
      int tmp = volatileVar; // volatile 读
      LoadLoadBarrier();    // 防止后续读重排序
      LoadStoreBarrier();   // 防止后续写重排序
      int b = x;            // 普通读(此时能看到线程1的 x=1)
      
  2. 缓存一致性协议

3,volatile 实现单例模式的双重锁(面手写)

双检锁/双重校验锁(DCL,即 double-checked locking)

**JDK 版本:**JDK1.5 起

**是否 Lazy 初始化:**是(即使用到这个变量时才会实例化)

**是否多线程安全:**是

**实现难度:**较复杂

**描述:**这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
getInstance() 的性能对应用程序很关键。

实例

public class Singleton {  private volatile static Singleton singleton;  private Singleton (){}  public static Singleton getSingleton() {  if (singleton == null) {  synchronized (Singleton.class) {  if (singleton == null) {  singleton = new Singleton();  }  }  }  return singleton;  }  
}
  • 私有构造器:禁止外部实例化
  • 双重检查
    • 第一次检查(无锁)
      避免每次调用 getSingleton() 都进入同步块,提升性能。
    • 第二次检查(加锁后)
      防止多个线程同时通过第一次检查后重复创建实例。
  • 同步锁(synchronized)
    • 保证 实例化过程的原子性,防止多线程并发创建多个实例。
  • volatile 关键字
    • 解决 指令重排序问题,确保其他线程不会获取到未初始化的对象。

如果不使用 volatile 关键字,JVM 可能会对这三个子步骤进行指令重排。

  • 为 Singleton对象分配内存
  • 将对象赋值给引用 singleton
  • 调用构造方法初始化成员变量

这种重排序会导致 singleton 引用在对象完全初始化之前就被其他线程访问到。具体来说,如果一个线程执行到步骤 2 并设置了 singleton 的引用,但尚未完成对象的初始化,这时另一个线程可能会看到一个“半初始化”的 Singleton对象。

  • 线程 A 执行到 if (singleton == null),判断为 true,进入同步块。
  • 线程 B 执行到 if (singleton == null),判断为 true,进入同步块。

如果线程 A 执行 singleton = new Penguin() 时发生指令重排序:

  • 线程 A 分配内存并设置引用,但尚未调用构造方法完成初始化。
  • 线程 B 此时判断 singleton != null,直接返回这个“半初始化”的对象。

这样就会导致线程 B 拿到一个不完整的 Penguin 对象,可能会出现空指针异常或者其他问题。

于是,我们可以为 singleton 变量添加 volatile 关键字,来禁止指令重排序,确保对象的初始化完成后再将其赋值给 singleton。

synchronized

1,基本用法

  • 加在静态方法上:锁定的是类

  • 加在非静态方法:锁定的是方法的调用者,当前实例。

  • 修饰代码块:锁定的是传入的对象

    并发学习之synchronized,JVM内存图,线程基础知识-CSDN博客

2,可重入性

从互斥锁的设计上来说,当一个线程试图操作一个由其他线程持有的对象锁的临界资源时,将会处于阻塞状态,但当一个线程再次请求自己持有对象锁的临界资源时,这种情况属于重入锁,请求将会成功。

synchronized 就是可重入锁,因此一个线程调用 synchronized 方法的同时,在其方法体内部调用该对象另一个 synchronized 方法是允许的。

3,Java对象头

Java对象在内存中的布局分为三部分:对象头(Header)实例数据(Instance Data)和 对齐填充(Padding)。

对象头是synchronized实现的基础,它包含两部分信息:Mark Word(标记字段)和 Klass Pointer(类型指针,指向对象的类元数据的指针,JVM通过这个指针确定对象是哪个类的实例)。

Mark Word 的格式:

锁状态29 bit 或 61 bit1 bit 是否是偏向锁?2 bit 锁标志位
无锁001
偏向锁线程 ID101
轻量级锁指向栈中锁记录的指针此时这一位不用于标识偏向锁00
重量级锁指向互斥量(重量级锁)的指针此时这一位不用于标识偏向锁10
GC 标记此时这一位不用于标识偏向锁11

synchronized的同步是基于进入和退出Monitor对象实现的,每个Java对象都与一个Monitor相关联。

那什么是Monitor对象

在不同的锁状态下,Mark word会存储不同的信息,这也是为了节约内存常用的设计。当锁状态为重量级锁(锁标识位=10)时,Mark word中会记录指向Monitor对象的指针,这个Monitor对象也称为管程监视器锁

在这里插入图片描述

每个对象都存在着一个 Monitor对象与之关联。执行 monitorenter 指令就是线程试图去获取 Monitor 的所有权,抢到了就是成功获取锁了;执行 monitorexit 指令则是释放了Monitor的所有权。

4,实现原理

JVM规范中对于synchronized的实现分为两种方式:代码块同步和方法同步,它们底层采用了不同的实现策略,但最终都可以归结为对Monitor对象的操作。

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

  1. 不是每个Java对象都有一个物理Monitor对象
    • 只有进入重量级锁状态时才会创建真正的Monitor对象
    • 偏向锁和轻量级锁阶段,锁信息存储在对象头中
  2. Monitor资源由JVM管理
    • Monitor对象不是Java层面的对象
    • 由JVM在需要时创建(通常位于C++层实现)

monitorenter 指令用于获取对象的监视器锁(Monitor lock),主要功能包括:

  1. 锁获取:尝试获取与对象关联的 Monitor
  2. 锁升级:根据竞争情况可能触发锁升级(偏向锁→轻量级锁→重量级锁)
  3. 重入计数:支持同一线程的锁重入
执行 monitorenter 时:
1. 检查对象头中的锁标志位- 如果是无锁状态(01):a. 尝试通过CAS将对象头Mark Word替换为当前线程指针(偏向锁)b. 成功则获取锁,失败则升级为轻量级锁- 如果是轻量级锁(00):a. 检查是否当前线程已持有锁(锁重入)b. 如果是,recursions+1c. 如果不是,自旋尝试获取或升级为重量级锁- 如果是重量级锁(10):a. 进入操作系统的互斥量等待队列
2. 获取成功后,对象头将记录锁状态和持有线程信息

monitorexit 指令用于释放对象的监视器锁,主要功能包括:

  1. 锁释放:释放对 Monitor 的持有
  2. 唤醒线程:在重量级锁状态下唤醒等待线程
  3. 重入处理:减少重入计数,只在完全释放时真正放开锁
执行 monitorexit 时:
1. 检查当前线程是否是锁的持有者- 如果不是,抛出 IllegalMonitorStateException
2. 减少重入计数(recursions)
3. 如果重入计数归零:a. 恢复对象头的无锁状态(轻量级锁)b. 或唤醒 EntryList 中的线程(重量级锁)
4. 如果是同步块结束处的 monitorexit:a. 正常退出同步区域
5. 如果是异常路径的 monitorexit:a. 仍然确保锁被释放b. 重新抛出异常

(1)代码块同步的实现

代码块同步是显式同步,通过monitorentermonitorexit指令实现:

  • 每个monitorenter必须有对应的monitorexit
  • 编译器会为同步块生成异常处理表,确保异常发生时也能释放锁
  • 可以针对任意对象进行同步

(2)方法同步的实现

方法同步是隐式同步,通过在方法访问标志中设置ACC_SYNCHRONIZED标志实现:

  • 调用方法时会隐式获取Monitor,没有显式的monitorentermonitorexit指令

  • 方法正常完成或异常抛出时会隐式释放Monitor

  • 同步的Monitor对象是方法所属的实例(非静态方法)或Class对象(静态方法)

  • JVM在方法调用时自动处理锁的获取和释放

特性monitorenter/monitorexitACC_SYNCHRONIZED
实现级别字节码指令方法访问标志
锁对象显式指定任意对象隐式使用 this 或 Class 对象
异常处理显式生成 monitorexitJVM 自动处理
可观察性可在字节码中直接看到只能通过访问标志识别
优化可能性较难优化更易被 JIT 优化

5,锁的升级与对比

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

在这里插入图片描述

  1. 偏向锁:

    • 设计目的:优化只有一个线程访问同步块的场景

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

    • 升级触发条件

      • 另一个线程尝试获取该锁(产生竞争)
      • 调用 hashCode() 方法(因为偏向锁会占用哈希码位置)
  2. 轻量级锁:

    • 设计目的:优化线程交替执行同步块的场景最适合少量线程(建议≤2个活跃竞争线程)和短时间同步的场景

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

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

    • 升级触发条件

      • CAS 操作失败(表示有竞争|两个线程的CAS操作出现重叠|竞争发生在同一时间窗口)
      • 自旋获取锁超过一定次数
  3. 重量级锁

    依赖于操作系统的互斥量(mutex) 实现的,而操作系统中线程间状态的转换需要相对较长的时间,所以重量级锁效率很低,但被阻塞的线程不会消耗 CPU。

原子操作的实现原理

1,术语

  • 缓存行:缓存行是CPU缓存的最小读写单位,通常为 64字节。三级缓存就是由缓存行组成。

    • L1/L2:每个核心独占,减少多核竞争。
    • L3:多核共享,避免频繁访问内存。
  • CAS:比较并且交换。CAS需要两个值,一个旧值,一个新值。旧值用来比较操作期间是否发生变化,如果没有发生变化才会交换新值。

  • CPU流水线技术

    时间轴  |  指令1   |  指令2   |  指令3   |  指令4   |
    --------+----------+----------+----------+----------+
    Cycle1  |   IF1    |          |          |          |
    Cycle2  |   ID1    |   IF2    |          |          |
    Cycle3  |   EX1    |   ID2    |   IF3    |          |
    Cycle4  |   MEM1   |   EX2    |   ID3    |   IF4    |
    Cycle5  |   WB1    |   MEM2   |   EX3    |   ID4    |
    Cycle6  |          |   WB2    |   MEM3   |   EX4    |
    Cycle7  |          |          |   WB3    |   MEM4   |
    Cycle8  |          |          |          |   WB4    |
    
  • 内存顺序冲突:内存顺序冲突 是由于 CPU/编译器优化导致的 指令重排问题导致的内存访问顺序与程序逻辑顺序不一致,从而引发数据竞争、逻辑错误等问题。

2,如何实现原子操作

  1. 总线锁
  2. 缓存锁:处理器标记该缓存行为 “锁定” 状态,阻止其他核心同时访问。

总线锁定把CPU和内存之间的通信锁住了,这使得锁定期间,其他处理器不能操作其他内存地址的数据,所以总线锁定的开销比较大,处理器在某些场合下使用缓存锁定代替总线锁定来进行优化。

有两种情况不适用缓存锁:

  • 操作的数据没有缓存在缓存行中,或者操作数据跨了多个缓存行会使用总线锁
  • 某些处理器不支持

3,Java如何实现原子操作

  • **AtomicInteger**等原子类

  • 使用volatile,synchronized关键字

  • 使用CAS循环实现原子操作

        /** * 使用CAS实现线程安全计数器 */private void safeCount() {for (;;) {int i = atomicI.get();boolean suc = atomicI.compareAndSet(i, ++i);if (suc) {break;}}}AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(100, 0);
    ref.compareAndSet(100, 101, stamp, stamp + 1);  // 检查值和版本号
    
CAS实现原子操作的三大问题
  • ABA问题:CAS在操作值时,如果一个值由A变为B又变为A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。解决思路是使用版本号,如上AtomicStampedReference
  • 循环时间长CPU开销大,自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。
  • 只能保证一个共享变量的原子操作,如果是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁。当多个线程同时竞争同一变量时,大量 CAS 操作会失败,导致线程自旋(循环重试)。自旋期间线程持续占用 CPU,执行无效循环,消耗 CPU 周期。

假设有1000线程并且这个CPU切换比较快速,其中一个CAS成功了,那剩余的999个就都白计算了,还不如加锁禁止其他线程操作,这样不会造成CPU的剧烈浪费。所以CAS只适合低烈度的并发。


http://www.hkcw.cn/article/iLYyNqfWZp.shtml

相关文章

Arch安装botw-save-state

devkitPro https://blog.csdn.net/qq_39942341/article/details/148387077?spm1001.2014.3001.5501 cargo https://blog.csdn.net/qq_39942341/article/details/148387783?spm1001.2014.3001.5501 megaton https://blog.csdn.net/qq_39942341/article/details/148388164?spm…

15-2021剑侠情缘2-各种修复完善+虚拟机单机端+外网服务端整理+文本教程+视频教程

任务完善 泉州三大BOSS 剑荡燕云 藏剑 通天顶 梁上等————–

css使用scoped之后样式失效问题

项目中的vue代码原本用的style标签来写css&#xff0c;现在想改成<style langscss scoped>&#xff0c;但是改完之后发现样式不对&#xff1a; 原来是&#xff1a; 将style改成scoped之后变成了&#xff1a;检查发现是之前定义的一些变量无法被识别&#xff0c;导致这些样…

大模型的开发应用(六):使用 Xtuner QLoRA 微调模型

这里写目录标题 0 前言1 Xtuner 简介1.1 主要特点1.2 核心功能1.3 优势与不足1.4 安装 2 数据集格式2.1 开源数据集2.2 自定义数据集2.3 数据集存放位置 3 微调大模型3.1 Qwen1.5的QLoRA配置文件3.2 修改配置文件&#xff08;1&#xff09;PART 1 基本设置&#xff08;2&#x…

cursor如何开启自动运行模式

在Cursor中&#xff0c;开启自动运行模式即启用“Yolo Mode”&#xff0c;具体操作如下&#xff1a; 按下Ctrl Shift J&#xff08;Windows/Linux&#xff09;或Cmd Shift J&#xff08;Mac&#xff09;打开Cursor设置。导航到“Features”&#xff08;功能&#xff09;选…

Visual Studio 2022 加载解决方案缓慢

Visual Studio 2022 加载解决方案加载缓慢 1.进入工具选项卡2.修改环境中的预览功能3.修改文本编辑器中的C#对应的高级选项 1.进入工具选项卡 工具 -> 选项 2.修改环境中的预览功能 环境 -> 预览功能 -> 更快加载项目&#xff08;某些功能可能会延迟&#xff09; 3.…

基于TMC5160 StallGuard2技术的工件搬运与尺寸检测融合系统

点击下面图片带您领略全新的嵌入式学习路线 &#x1f525;爆款热榜 90万阅读 1.6万收藏 1 系统设计目标与创新价值 在现代智能制造系统中&#xff0c;传统自动化产线面临一个普遍存在的技术痛点&#xff1a;工件搬运与尺寸检测通常需要分离的子系统完成。这种分离不仅增加了…

github 提交失败,连接不上

1. 第一种情况&#xff0c;开了加速器&#xff0c;导致代理错误 删除hosts文件里相关的github代理地址 2. 有些ip不支持22端口连接,改为443连接 ssh -vT gitgithub.com // 命令执行结果 OpenSSH_for_Windows_9.5p1, LibreSSL 3.8.2 debug1: C…

智慧政务标准规范介绍:构建高效、协同的政务信息体系

在当今信息化快速发展的时代&#xff0c;智慧政务作为政府数字化转型的重要方向&#xff0c;正逐步改变着政府管理和服务的方式。为了确保智慧政务系统的建设能够有序、高效地进行&#xff0c;国家制定了一系列标准规范&#xff0c;其中GB∕T 21062系列标准《政务信息资源交换体…

77页中国金融体系指标大全(2024年版)【附全文阅读】

本文主要介绍了金融业通行宝典中国金融体系指标大全的内容&#xff0c;包括央行体系、商业银行体系、非银金融机构与地方金融组织的各项指标。文章详细分析了美联储资产负債表的结构&#xff0c;并概述了美日欧等主要经济体资产负债表状况。 重点内容&#xff1a; 1. 央行体系是…

Java进阶之不可变对象:用法实例(六十八)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…

一次借助ChatGPT抵御恶意攻击的经历,为个人服务器添加自动防御系统Fail2ban

title: 一次借助ChatGPT抵御恶意攻击的经历&#xff0c;为个人服务器添加自动防御系统Fail2ban tags: 个人成长 categories:杂谈 我有一台个人服务器&#xff0c;托管着自己的WordPress网站&#xff0c;也放了RustDesk这种私有化的远程桌面工具&#xff0c;最近我发现RustDesk…

数论算法入门

目录 模运算 快速幂 gcd和lcm exgcd 同余与逆元 模运算 快速幂 迭代&#xff08;原理&#xff1a;位运算&#xff09; #include <iostream> using namespace std; int power(int a, int n) {int ans 1;while (n){if (n % 2 1){ans * a;}n / 2;a * a;}return ans…

RocketMQ 消息发送核心源码解析:DefaultMQProducerImpl.send () 方法深度剖析

引言 在分布式系统中&#xff0c;消息队列是实现异步通信、服务解耦和流量削峰的关键组件。Apache RocketMQ 作为一款高性能、高可靠的消息中间件&#xff0c;被广泛应用于各类互联网场景。其中&#xff0c;消息发送是最基础也是最重要的功能之一。本文将深入剖析 RocketMQ 中…

计算机视觉NeRF

NeRF与3DGS学习 NeRF计算机视觉的问题NeRF定义神经辐射场场景表示基于辐射场的体渲染分层采样优化神经辐射场 基础知识初始化SFM基础矩阵 & 本质矩阵 & 单应矩阵从已经估得的本质矩阵E&#xff0c;恢复出相机的运动R,tSVD 分解 NeRF NeRF资源 计算机视觉的问题 计算…

分班 - 华为OD统一考试(JavaScript 题解)

华为OD机试题库《C》限时优惠 9.9 华为OD机试题库《Python》限时优惠 9.9 华为OD机试题库《JavaScript》限时优惠 9.9 针对刷题难&#xff0c;效率慢&#xff0c;我们提供一对一算法辅导&#xff0c; 针对个人情况定制化的提高计划&#xff08;全称1V1效率更高&#xff09;。 看…

CVE-2021-28169源码分析与漏洞复现(Jetty信息泄露)

漏洞概述 漏洞名称&#xff1a;Jetty ConcatServlet 多重解码导致 WEB-INF 敏感信息泄露 漏洞编号&#xff1a;CVE-2021-28169 CVSS 评分&#xff1a;7.5 影响版本&#xff1a; Jetty 9.4.0 - 9.4.39Jetty 10.0.0 - 10.0.1Jetty 11.0.0 - 11.0.1 修复版本&#xff1a;Jetty ≥…

CLion调试无法触发断点

CLion 调试时执行的是cmake-build-release目录中的exe&#xff0c;无法触发断点 这里配置要选择Debug&#xff0c;不要选择Release

2024年数维杯国际大学生数学建模挑战赛C题时间信号脉冲定时噪声抑制与大气时延抑制模型解题全过程论文及程序

2024年数维杯国际大学生数学建模挑战赛 C题 时间信号脉冲定时噪声抑制与大气时延抑制模型 原题再现&#xff1a; 脉冲星是一种快速旋转的中子星&#xff0c;具有连续稳定的旋转&#xff0c;因此被称为“宇宙灯塔”。脉冲星的空间观测在深空航天器导航和时间标准维护中发挥着至…

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Sound Board(音响控制面板)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— SoundBoard 组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ &#x1f3af; 组件目标 实现一个响应式按钮面板&#xff0c;点…