深入解析Java21核心新特性(虚拟线程,分代 ZGC,记录模式模式匹配增强)

news2025/6/9 15:20:49

文章目录

  • 前言
  • 一、虚拟线程 (Virtual Threads - JEP 444) - 并发的革命
    • 1.1 解决的核心问题🎯
    • 1.2 工作原理与核心机制⚙️
    • 1.3 使用详解与最佳实践🛠️
    • 1.4 注意事项⚠️
    • 1.5 总结 📚
  • 二、分代 ZGC (Generational ZGC - JEP 439) - 低延迟新高度
    • 2.1 解决的核心问题🎯
    • 2.2 工作原理与核心机制⚙️
    • 2.3 使用详解与最佳实践🛠️
    • 2.4 注意事项⚠️
    • 2.5 总结 📚
  • 三、记录模式 (Record Patterns - JEP 440) & 模式匹配增强
    • 3.1 解决的核心问题🎯
    • 3.2 工作原理与核心机制⚙️
    • 3.3 使用详解与最佳实践🛠️
    • 3.4 注意事项⚠️
    • 3.5 总结 📚
  • 总结


前言

Java 21 作为最新的长期支持 (LTS) 版本,于 2023 年 9 月发布,带来了多项革命性特性和重要改进,本文将深入探讨其核心新特性。


一、虚拟线程 (Virtual Threads - JEP 444) - 并发的革命

虚拟线程是 Java 21 最重大的革新,从根本上重塑了 Java 的并发编程模型,解决了传统线程模型的根本性瓶颈。

1.1 解决的核心问题🎯

  1. 海量线程瓶颈
    • 传统 OS 线程(平台线程)内存开销大(约 1MB/线程)
    • 创建/切换成本高(涉及内核调度)
    • 典型服务器只能支撑 1000-5000 并发线程
  2. 复杂异步编程陷阱
    • CompletableFuture 和回调模式导致"回调地狱"
    • 堆栈跟踪困难,调试复杂度指数级上升
    • 线程池配置与资源管理成为高难度技能
  3. 阻塞操作资源浪费
// 传统线程模型下的阻塞操作
Thread.sleep(1000); // 线程被挂起但占用完整内存
socket.read();      // 内核态阻塞,CPU空转等待

在 I/O 等待期间线程被阻塞,但系统仍需为其维护完整的线程栈

虚拟线程解决了Java高并发场景下的线程资源瓶颈编程复杂度两大核心问题:它通过轻量级的JVM级线程实现(仅占几百字节),突破了传统操作系统线程的内存开销和创建数量限制,使得单机轻松支持百万级并发;同时允许开发者使用直观的同步代码风格编写高并发程序,既避免了回调地狱的复杂性,又显著提升了I/O密集型应用的吞吐量(实测可达传统线程池的10倍以上),真正实现了"编写同步代码,获得异步性能"的理想效果。

1.2 工作原理与核心机制⚙️

  1. M:N 线程模型:
    M:N 线程模型
    M:N线程模型(也称混合线程模型)是一种将大量用户级线程(M个虚拟线程)复用到少量内核级线程(N个平台线程)上的并发调度机制,由JVM而非操作系统负责线程调度:虚拟线程在遇到I/O阻塞时会自动挂起并释放底层平台线程,使一个平台线程可高效轮换执行多个虚拟线程,既保留了轻量级线程的创建优势(低内存开销、支持百万级并发),又充分利用了多核CPU的计算资源(通过少量平台线程绑定CPU核心)。

  2. 协作式调度机制

    • 虚拟线程在遇到阻塞操作时自动挂起
    • JVM 将挂起的虚拟线程从平台线程卸载
    • 就绪的虚拟线程被调度到空闲平台线程执行
    • 挂起时仅保留极小堆栈帧(约 200 字节)
  3. 载体线程(Carrier Thread)

    • 平台线程作为虚拟线程的运行载体
    • JVM 内置的 ForkJoinPool 默认管理载体线程
    • 数量通常等于 CPU 核心数

1.3 使用详解与最佳实践🛠️

基础创建方式:

// 1. 直接启动虚拟线程
Thread vt = Thread.startVirtualThread(() -> {
    System.out.println("虚拟线程运行中");
});

// 2. 使用Builder精确配置
Thread.ofVirtual()
      .name("order-processor-", 0)
      .start(() -> processOrder(order));

// 3. 虚拟线程池(推荐)
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
executor.submit(() -> handleRequest(request));

与传统代码互操作:

// 在虚拟线程中使用ThreadLocal
ThreadLocal<String> userContext = new ThreadLocal<>();

executor.submit(() -> {
    userContext.set("user123"); // 正常使用
    
    // 调用传统同步代码
    legacySynchronousMethod(); // 无兼容问题
    
    System.out.println(userContext.get()); // 输出 "user123"
});

虚拟线程的轻量级特性高频创建会放大 ThreadLocal 的内存泄漏风险,因为虚拟线程生命周期可能极短,但 ThreadLocal 的值会一直存活(直到线程终止或显式清除)。

高级调度控制:

// 定制虚拟线程调度器
ExecutorService customExecutor = Executors.newThreadPerTaskExecutor(
    Thread.ofVirtual()
          .scheduler(myCustomScheduler) // 自定义调度器
          .factory()
);

// 绑定到特定载体线程(特殊场景)
try (var carrier = Executors.newSingleThreadExecutor()) {
    Thread.ofVirtual().scheduler(carrier)
          .start(() -> {
              // 此虚拟线程始终在同一个载体线程运行
          });
}

1.4 注意事项⚠️

  1. 避免在虚拟线程中使用 synchronized 和 Thread.sleep()
    • synchronized 会阻塞底层平台线程(Carrier Thread),导致线程池资源耗尽。
    • Thread.sleep() 也会固定占用载体线程,降低并发性能。

解决方案:
✅ 改用 ReentrantLock 替代 synchronized(允许虚拟线程挂起):

Lock lock = new ReentrantLock();
lock.lock();
try {
    // 临界区代码
} finally {
    lock.unlock(); // 确保释放锁
}

✅ 使用 LockSupport.parkNanos() 或 Thread.yield() 替代 Thread.sleep()(非阻塞等待):

LockSupport.parkNanos(1_000_000_000); // 1秒(不阻塞载体线程)
  1. 谨慎使用 ThreadLocal,防止内存泄漏
    • 虚拟线程生命周期短,但 ThreadLocal 数据会持续占用内存,直到线程终止。
    • 大量虚拟线程未清理 ThreadLocal 可能导致 OOM(内存溢出)。

解决方案:
✅ 始终在 try-finally 中清理 ThreadLocal:

ThreadLocal<String> userContext = new ThreadLocal<>();
try {
    userContext.set("Alice");
    // ...业务逻辑
} finally {
    userContext.remove(); // 强制清理
}

✅ 优先使用 ScopedValue(Java 21+)(自动管理生命周期):

ScopedValue<String> userContext = ScopedValue.newInstance();
ScopedValue.where(userContext, "Alice")
           .run(() -> System.out.println(userContext.get())); // 自动释放
  1. 避免长时间占用 CPU(防止线程固定)
    • 虚拟线程适合 I/O 密集型任务(如 HTTP 请求、DB 查询)。
    • 如果长时间执行 CPU 密集型计算,会固定到载体线程,降低吞吐量。

解决方案:
✅ 拆分 CPU 密集型任务,使用 ExecutorService 单独处理:

ExecutorService cpuExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
ExecutorService virtualExecutor = Executors.newVirtualThreadPerTaskExecutor();

virtualExecutor.submit(() -> {
    // I/O 操作(适合虚拟线程)
    String data = fetchDataFromDB();
    
    // CPU 密集型计算(提交到专用线程池)
    Future<Integer> result = cpuExecutor.submit(() -> heavyCompute(data));
    System.out.println(result.get());
});
  1. 不要手动管理虚拟线程池
    • 虚拟线程本身极其轻量,不需要池化(传统 ThreadPoolExecutor 不适用)。
    • 手动管理虚拟线程池会增加复杂度,甚至降低性能。

解决方案:
✅ 直接使用 Executors.newVirtualThreadPerTaskExecutor()(JVM 自动优化):

try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 100_000; i++) {
        executor.submit(() -> processRequest(i)); // 自动管理线程
    }
} // 自动关闭

1.5 总结 📚

虚拟线程 是 Java 21 引入的轻量级并发模型,通过 M:N 线程调度(百万级虚拟线程复用少量平台线程)彻底解决了传统线程模型的 高内存开销低并发上限 问题,使开发者能用 同步代码风格 轻松实现 高吞吐异步性能,尤其适合 I/O 密集型场景(如微服务、数据库访问)。使用时需注意:
① 避免 synchronized 改用 ReentrantLock 防止载体线程阻塞;
② 谨慎管理 ThreadLocal 防止内存泄漏;
③ 分离 CPU 密集型任务;
④ 直接使用虚拟线程池(newVirtualThreadPerTaskExecutor())而非手动池化。
这一革新让 Java 并发编程回归直观,同时支撑云原生时代的百万级并发需求。

二、分代 ZGC (Generational ZGC - JEP 439) - 低延迟新高度

分代 ZGC(Generational ZGC)是 Java 21 针对低延迟垃圾回收的重大革新,它在保留原始 ZGC 亚毫秒级停顿时间优势的同时,显著降低了 GC 开销,解决了大规模应用场景下的吞吐量和内存占用问题。

2.1 解决的核心问题🎯

  1. 原始 ZGC 的吞吐量瓶颈
    • 无分代设计导致每次 GC 均需扫描整个堆内存(无论对象年龄)。
    • 长生命周期对象被反复扫描(占堆内存 70% 以上)。
  2. 内存占用压力
    • 需额外 15%-20% 堆空间维持低停顿(指针着色技术)。
    • 大堆应用(如 100GB+)内存成本显著上升。
  3. 高并发场景的资源争抢
    高并发场景的资源争抢
    全堆扫描导致 CPU 缓存命中率降低,加剧线程竞争。

总结:
在 Java 21 之前,ZGC 虽然实现了亚毫秒级停顿(<1ms),但因其无分代设计导致两个核心痛点:

  • 吞吐量损失:每次回收都需扫描全堆,长生命周期对象被反复检查,造成 30%-40% 的额外 CPU 开销;
  • 内存压力大:依赖指针着色技术需额外占用 15%-20% 堆空间,且大堆场景(如 100GB+)下内存利用率低下。这些问题使 ZGC 在高吞吐需求场景(如数据分析)和资源受限环境(如容器)中难以普及,直到 Java 21 的分代 ZGC 通过代际分离彻底解决。

2.2 工作原理与核心机制⚙️

  1. 堆空间划分
// 分代 ZGC 堆结构(逻辑隔离)
+---------------------+
|   Young Generation  |  // 占堆 10%-30%(默认自适应)
|     (Eden+S0/S1)    |  // 存放新对象
+---------------------+
|   Old Generation    |  // 存放长期存活对象
+---------------------+
  1. 分代收集策略
收集类型触发条件工作范围停顿时间
Young GCEden 区满仅新生代<0.5ms
Old GC老年代占用达阈值仅老年代<1ms
Full GC内存分配失败(极罕见)整个堆<10ms
  1. 关键技术优化
  • 染色指针保留:仍使用 4TB 虚拟地址映射(42 位指针)实现并发标记。
  • 负载屏障优化:老年代 → 新生代引用不触发屏障(减少 70% 屏障调用)。
  • 代间引用追踪:使用 卡表(Card Table) 记录跨代引用,加速老年代回收。

2.3 使用详解与最佳实践🛠️

  1. 启用分代 ZGC(Java 21+):
java -XX:+UseZGC -Xmx16g -Xlog:gc* MyApp.java

从 Java 21 开始,-XX:+UseZGC 默认启用分代模式

  1. 关键调优参数
参数默认值说明
-XX:ZGenerationaltrue显式启用/禁用分代(默认 true)
-XX:NewRatio2老年代/新生代比例(Old:Young=2:1)
-XX:ZCollectionInterval5GC 触发间隔(秒)
-XX:ZAllocationSpikeTolerance2.0内存分配速率容忍因子
  1. 监控命令
# 查看分代收集详情
jstat -gcutil <pid> 1s

# 生成 GC 报告
java -Xlog:gc*=debug:file=gc.log -XX:+UseZGC MyApp

2.4 注意事项⚠️

  1. 新生代大小调优
    • 过小 → Young GC 频繁(建议占堆 15%-25%)。
    • 过大 → Old GC 延迟升高(老年代挤压)。
# 动态调整示例(设置新生代最小1G/最大4G)
-XX:MinNewSize=1g -XX:MaxNewSize=4g
  1. 混合工作负载优化
// 对象分配策略建议:
if (object.isShortLived()) {
    // 优先分配在新生代(减少老年代碎片)
} else {
    // 直接晋升老年代(避免多次Young GC复制)
}
  1. 规避全堆扫描
    • 避免 System.gc() 调用(使用 -XX:+DisableExplicitGC)。
    • 超大对象(>4MB)直接进入老年代(-XX:ZLargeObjectSizeLimit)。
  2. 与虚拟线程协同
    虚拟线程&垃圾回收
    虚拟线程的轻量级特性与分代 ZGC 完美契合,共同实现 高并发 + 低延迟。

2.5 总结 📚

ZGC 收集器通过引入分代机制实现了质的飞跃:它将堆划分为新生代和老年代,Young GC 仅回收新生代短命对象(停顿<0.5ms),Old GC 专注老年代(停顿<1ms),同时保留并发标记-整理特性,配合卡表优化跨代引用扫描。这种设计在保持亚毫秒级停顿优势的同时,显著降低了40%以上的GC开销,吞吐量提升超50%,使大内存应用(如百GB级堆)也能兼顾极致低延迟和高吞吐,成为云原生时代Java应用的GC终极选择。

三、记录模式 (Record Patterns - JEP 440) & 模式匹配增强

3.1 解决的核心问题🎯

  1. 数据解构样板代码泛滥:传统 Java 在提取嵌套对象数据时需要层层类型检查和强制转换
if (obj instanceof Point) {
    Point p = (Point) obj;
    if (p.getColor() != null) {
        Color c = p.getColor();
        System.out.println(c.rgb());
    }
} // 金字塔式缩进,可读性差
  1. 类型匹配与数据访问割裂:instanceof 只做类型检查,获取字段需额外操作(如调用 getter 或强转)。
  2. 嵌套数据处理复杂度高:处理类似 Order(User(Payment(…))) 的深层嵌套结构时,代码急剧膨胀。

3.2 工作原理与核心机制⚙️

  1. 模式解构
record Point(int x, int y) {}

// 旧方式:类型检查+字段提取分离
if (obj instanceof Point) {
    Point p = (Point) obj;
    int x = p.x();
    int y = p.y();
}

// 新方式:类型检查与解构合一
if (obj instanceof Point(int x, int y)) { 
    // 直接使用解构出的 x, y
}

编译器自动将 Point(int x, int y) 编译为:

  • 检查 obj 是否为 Point 类型
  • 提取字段值并绑定到变量 x, y
  1. 类型投影
// 嵌套记录结构
record User(String name, Address address) {}
record Address(String city, String street) {}

// 深度解构:一步提取底层字段
if (user instanceof User(String name, Address(String city, _))) {
    System.out.println(name + " in " + city);
}
  • _ 表示忽略该字段(未命名变量)
  • 编译器自动处理多层类型检查和字段绑定
  1. Switch 的模式化改造
// 旧版:仅支持常量匹配
switch (obj) {
    case Integer i -> ...;
    case String s  -> ...;
    default        -> ...;
}

// 新版:支持记录模式和守卫条件
return switch (shape) {
    case Circle c when c.radius() > 10 -> "Large Circle";
    case Circle _                       -> "Small Circle";
    case Rectangle r                    -> "Area: " + (r.width() * r.height());
    case null                           -> "Null shape"; // 显式处理null
};

模式匹配的编译流程:
模式匹配

3.3 使用详解与最佳实践🛠️

  1. 嵌套记录解构
record Order(String id, User user, double amount) {}
record User(String id, Address address) {}
record Address(String city) {}

void processOrder(Object obj) {
    if (obj instanceof Order(_, User(_, Address(var city)), var amt)) {
        System.out.println("订单来自: " + city + ", 金额: " + amt);
    }
}
  1. 泛型记录支持
record Box<T>(T content) {}

static void unbox(Box<?> box) {
    if (box instanceof Box<String>(var s)) {
        System.out.println("String: " + s);
    } else if (box instanceof Box<Integer>(var i)) {
        System.out.println("Integer: " + i);
    }
}
  1. 模式匹配 + Sealed 类(穷尽性检查)
sealed interface Shape permits Circle, Rectangle {}

record Circle(double radius) implements Shape {}
record Rectangle(double w, double h) implements Shape {}

double area(Shape s) {
    return switch (s) {
        case Circle c    -> Math.PI * c.radius() * c.radius();
        case Rectangle r -> r.w() * r.h();
        // 无需default:编译器检查所有permits类型已覆盖
    };
}
  1. 守卫条件
Object obj = ...;
switch (obj) {
    case String s when s.length() > 5 -> 
        System.out.println("长字符串: " + s);
    case String s                     -> 
        System.out.println("短字符串: " + s);
    case Integer i when i > 100       ->
        System.out.println("大整数: " + i);
    default -> {}
}

3.4 注意事项⚠️

  1. 优先使用 record 而非普通类
// 传统类无法自动解构!
class OldPoint { int x; int y; } 
// 需手动实现解构模式(复杂)

// 记录类直接支持
record NewPoint(int x, int y) {} 
  1. 避免过度嵌套
// 超过3层的解构可读性下降
case A(B(C(D var d))) → 重构为独立方法
  1. 空值处理策略
// 方案1:显式处理null
switch (obj) {
    case null -> ... 
    case Point p -> ...
}

// 方案2:禁止null(推荐)
Objects.requireNonNull(obj);

3.5 总结 📚

记录模式是Java数据处理的范式革新,它通过结构化解构语法彻底改变了Java操作复杂数据的方式。该特性包含三大突破:

  • 声明式解构:使用instanceof Point(int x, int y)语法,将类型检查、字段提取和变量绑定原子化完成,消灭了传统Java中冗长的类型转换和getter调用链条。
  • 深度模式匹配:支持递归解构嵌套记录,如Order(User(Address(var city),_))可直达深层数据,配合switch表达式实现类型安全的模式分支,编译器会强制检查穷尽性(尤其与sealed类配合时)。
  • 上下文智能绑定:通过var推导和_忽略符,使代码既保持强类型安全又极度简洁,实测能使数据转换类代码缩减60%以上。

总结

Java 21 三大革新总结:

  • 虚拟线程:革命性解决高并发瓶颈,通过轻量级线程(单机百万级)和同步式编码实现异步性能,吞吐量提升10倍+,但需规避synchronized阻塞载体线程;
  • 分代ZGC:在保留亚毫秒停顿的同时,引入新生代/老年代分区回收,降低40% GC开销,提升50%吞吐量,百GB堆内存利用率显著优化;
    记录模式:以instanceof Point(int x, int y)原子化完成类型检查与数据解构,支持嵌套记录深度匹配,使数据导航代码减少70%,尤其与sealed类结合实现编译期穷尽检查。三者共同奠定Java在高并发、低延迟、数据密集型场景的统治级优势。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2405515.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

免费批量去水印工具 - 针对文心一言生成图片

免费批量去水印工具 - 针对文心一言生成图片 工具介绍 这是一款免费的批量去水印工具&#xff0c;专门针对文心一言生成的图片进行处理。通过简单的操作&#xff0c;您可以快速去除图片中的水印。 下载链接 您可以通过以下网盘链接下载工具&#xff1a; 链接: https://pa…

在WPS中如何启用宏VBA wps.vba.exe下载和安装

首先我们点击导航栏中的【工具】&#xff0c;点击左侧 运行宏&#xff0c;根据提示 点击 立即加载。加载卡在50%时间比较长&#xff0c;耐心等待。 关闭wps重新打开后&#xff0c; word和xls表格都可以使用了。 如果电脑无法联网&#xff0c;需要提前下载 WPS VBA插件 WPS VB…

Hardware-Efficient Attention for Fast Decoding

TL;DR 2025 年普林斯顿大学提出的硬件友好 attention 设计&#xff0c;在 MQA/GQA 与 deepseek 提出的 MLA 基础之上继续优化&#xff0c;提出 Grouped-Tied Attention (GTA) 和 Grouped Latent Attention (GLA)&#xff0c;实现更高推理效率的同时也能保持较好的模型效果。 …

LLMs 系列科普文(13)

十三、AlphaGO 提到强化学习的历史&#xff0c;不得不提到 alphago&#xff0c;如果你不记得这是什么了&#xff0c;那你是否还曾记得&#xff0c;早些年 AI 已经可以在围棋中击败人类选手了。 AlphaGO 系统又 DeepMind 公司开发&#xff0c;你可以在网络上找到当初人机大战的…

element-plus 单选组件 el-radio,选不上,又没报错,直接复制官网也不行解决方案

在使用 Vue 框架开发项目时&#xff0c;Element UI 是常用的组件库。最近在开发中遇到了 Element 单选框组件el-radio的双向绑定问题&#xff0c;直接复制element官网上的的案例下来也是不得&#xff0c;经过调试和探索&#xff0c;终于找到了解决方案&#xff0c;特此记录分享…

idea 启动jar程序并调试

添加一个JAR 应用程序&#xff0c;填写以下内容&#xff1a; JAR路径&#xff1a;填写你要启动的jar程序的绝对路径 虚拟机选项&#xff1a;-Xmx1G -Xms1G -agentlib:jdwptransportdt_socket,servery,suspendn,address*:5005 程序实参&#xff08;可选&#xff0c;minecraft专用…

CSS 轮廓(Outline)与边框(Border)的深度解析

在 CSS 中&#xff0c;轮廓&#xff08;outline&#xff09;和边框&#xff08;border&#xff09;是两个用于装饰元素的重要属性&#xff0c;但它们在功能、渲染机制和应用场景上存在显著差异。下面从多个维度进行详细对比&#xff1a; 一、基础定义与语法差异 边框&#xf…

Docker 部署 Python 的 Flask项目

文章目录 一、构建运行 Docker 容器1. 查找合适镜像2.本地docker 拉取镜像3.项目配置1. python项目下生成 requirements.txt 依赖文件2. 生成Dockerfile文件3.忽略不必要文件4. 构建镜像 4. 运行容器5.测试 二、常见问题与解决方案 一、构建运行 Docker 容器 1. 查找合适镜像 …

Vue入门到实战之第一篇【超基础】

Vue入门到实战之第一篇 学习路线1. Vue 概念1.1 Vue 是什么 2. 创建Vue实例&#xff0c;初始化渲染3. 插值表达式 {{ }}4. Vue响应式特性5. 开发者工具 学习路线 1. Vue 概念 1.1 Vue 是什么 概念&#xff1a; Vue是一个用于 构建用户界面1 的 渐进式2 框架3 1&#xff1a;基…

实时数据分析的技术架构:Lambda vs Kappa架构选择

文章目录 引言:实时数据分析架构的重要性Lambda架构深度解析Kappa架构技术特性架构对比分析维度性能与可扩展性评估技术栈选型指南实际应用场景分析成本效益对比模型混合架构与演进策略企业级决策框架最佳实践与案例研究技术趋势与未来展望引言:实时数据分析架构的重要性 在…

springboot2.x升级springboot3.x

springboot2.x升级springboot3.x 背景升级jdk版本为17以上springboot版本修改javax包更新mybatis-plus升级swagger升级springdocspringdoc配置 背景 当前项目是springboot2.5.9版本的springbootmybatis-plus项目&#xff0c;需要升级到springboot3.5.0项目。 升级jdk版本为17…

Python训练打卡Day43

复习日 1.卷积神经网络的基本概念 2.kaggle找到一个图像数据集&#xff0c;用cnn网络进行训练并且用grad-cam做可视化 进阶&#xff1a;并拆分成多个文件 tips:注册kaggle的注意事项 安装插件&#xff1a;Header Editor 然后打开扩展选项&#xff1a; 输入网址&#xff1a;ht…

227.2018年蓝桥杯国赛 - 交换次数(中等)- 贪心

227. 交换次数&#xff08;贪心&#xff09; 1. 2018年蓝桥杯国赛 - 交换次数&#xff08;中等&#xff09; 标签&#xff1a;2018 暴力 国赛 1.1 题目描述 IT 产业人才需求节节攀升。业内巨头百度、阿里巴巴、腾讯&#xff08;简称 BAT &#xff09;在某海滩进行招聘活动。…

STM32入门学习之系统时钟配置

1. 时钟就是单片机的心脏。单片机根据时钟频率来控制每个部件的工作&#xff0c;时钟是单片机的脉搏&#xff0c;决定了每条命令运行的速率&#xff0c;没有时钟单片机将停止工作。 如何理解“时钟决定了单片机每条命令运行的速率”&#xff1f; 首先需要去理解单片机中的时…

【ArcGIS Pro微课1000例】0072:如何自动保存编辑内容及保存工程?

文章目录 一、自动保存编辑内容二、自动保存工程在使用ArcGIS或者ArcGIS Pro时,经常会遇到以下报错,无论点击【发送报告】,还是【不发送】,软件都会强制退出,这时如果对所操作没有保存,就会前功尽弃。 此时,自动保存工作就显得尤为重要,接下来讲解两种常见的自动保存方…

AU音频软件|Audition 2025网盘下载与安装教程指南

说起AU&#xff0c;有些小伙伴可能第一印象是化学元素金&#xff08;Aurum&#xff09;。实际上&#xff0c;本文要介绍的AU&#xff0c;全称是Adobe Audition&#xff0c;是一款专业音频编辑和混音软件‌&#xff0c;广泛应用于音乐制作、广播、电影及视频声音设计等领域。 目…

网络编程(TCP编程)

思维导图 1.基础流程 流程图中是TCP连接的基础步骤&#xff0c;其他操作都是在此基础上进行添加修改。 2.函数接口 2.1 创建套接字&#xff08;socket&#xff09; int socket(int domain, int type, int protocol); 头文件&#xff1a;#include <sys/types.h> …

热成像实例分割电力设备数据集(3类,838张)

在现代电力系统的运维管理中&#xff0c;红外热成像已经成为检测设备隐患、预防故障的重要手段。相比传统可见光图像&#xff0c;红外图像可揭示设备温度分布&#xff0c;从而更直观地反映过热、老化等问题。而在AI赋能下&#xff0c;通过实例分割技术对热成像中的电力设备进行…

用电脑通过USB总线连接控制keysight示波器

通过USB总线控制示波器的优势 在上篇文章我介绍了如何通过网线远程连接keysight示波器&#xff0c;如果连接的距离不是很远&#xff0c;也可以通过USB线将示波器与电脑连接起来&#xff0c;实现对示波器的控制和截图。 在KEYSIGHT示波器DSOX1204A的后端&#xff0c;除了有网口…

uni-app学习笔记二十四--showLoading和showModal的用法

showLoading(OBJECT) 显示 loading 提示框, 需主动调用 uni.hideLoading 才能关闭提示框。 OBJECT参数说明 参数类型必填说明平台差异说明titleString是提示的文字内容&#xff0c;显示在loading的下方maskBoolean否是否显示透明蒙层&#xff0c;防止触摸穿透&#xff0c;默…