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

ThreadLocal源码深度剖析:内存管理与哈希机制

ThreadLocal是Java并发编程中的重要工具,它为每个线程提供独立的变量存储空间,实现了线程之间的数据隔离。本文将从源码实现角度,深入分析ThreadLocal的内部机制,特别是强弱引用关系、内存泄漏问题、ThreadLocalMap的扩容机制以及相关的哈希原理,帮助读者全面理解这一重要类的实现细节。

线程局部变量的概念与应用场景

线程局部变量(Thread Local Variable)是一种特殊的变量,它与线程而非方法或类关联。每个访问该变量的线程都会看到自己独立的变量值,而不会看到其他线程的变量值。这种特性使得线程局部变量成为处理线程特定状态的理想选择。
在线程安全问题日益突出的多线程环境中,线程局部变量提供了一种简单而高效的方式来避免共享可变状态的问题。例如,在数据库连接管理、事务ID跟踪、用户会话信息存储等场景中,线程局部变量都能发挥重要作用。

ThreadLocal类的内部结构

ThreadLocal与Thread的关系

ThreadLocal的核心在于它与Thread类的紧密关联。每个Thread对象都包含一个ThreadLocalMap实例,用于存储该线程所有的线程局部变量。当一个线程访问ThreadLocal变量时,实际上是在访问该线程ThreadLocalMap中的对应条目。
这种设计使得线程局部变量的生命周期与线程的生命周期紧密相关。线程结束时,其ThreadLocalMap中的所有变量也会被释放,前提是引用关系正确处理。

内部类ThreadLocalMap

ThreadLocal的核心实现依赖于其内部类ThreadLocalMap。这个特殊的哈希表结构用于存储线程局部变量,其关键特性是使用弱引用(WeakReference)来存储键(即ThreadLocal对象)。

static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}private Entry[] table = new Entry[INITIAL_CAPACITY];private int size = 0;private int threshold = 0;private final float loadFactor;ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue, float lf) {loadFactor = lf;threshold = (int)(INITIAL_CAPACITY * lf);table[0] = new Entry(firstKey, firstValue);size = 1;}
}

从源码中我们可以看到,ThreadLocalMap使用了一个Entry数组作为其内部存储结构。每个Entry不仅包含一个弱引用的ThreadLocal对象作为键,还包含一个Object类型的值。

强弱引用关系分析

在ThreadLocalMap中,键是ThreadLocal对象的弱引用,而值是存储的具体对象。这种设计有其深刻的内存管理考量。

弱引用的作用与影响

弱引用是Java提供的一种引用强度较弱的引用类型。与普通强引用不同,持有弱引用的对象在垃圾回收时不会阻止对象被回收。(立即被回收)
在ThreadLocalMap中使用弱引用的目的是为了防止内存泄漏。如果ThreadLocal对象不再被使用(即没有强引用存在),它会被垃圾回收,同时释放与之关联的值。这确保了线程局部变量不会永久占用内存,特别是在线程生命周期结束时。

垃圾回收时机与内存泄漏风险

尽管使用了弱引用,ThreadLocal仍然存在潜在的内存泄漏风险。如果线程局部变量的值持有较大对象,而线程生命周期很长(如守护线程),这些值可能会在ThreadLocal对象被回收之前长时间存在。
此外,如果应用程序频繁创建新的ThreadLocal实例但不正确地释放它们,也可能导致内存问题。这是因为每个新的ThreadLocal实例都需要在每个活跃线程的ThreadLocalMap中创建一个条目,即使这些线程不再需要这些变量。

内存泄漏的根本原因

ThreadLocal导致内存泄漏的主要原因有以下几点:

  1. 线程生命周期过长:如果线程长时间运行(如守护线程),而线程局部变量未被正确释放,这些变量会一直存在于线程的ThreadLocalMap中。
  2. 不正确的引用关系:如果线程局部变量的值包含对其他对象的强引用,这些对象不会被垃圾回收,直到线程局部变量被移除。
  3. 静态ThreadLocal变量:静态的ThreadLocal变量生命周期与类加载器相关,可能导致意外的内存保留。

防范与解决内存泄漏的策略

  1. 显式调用remove()方法:在不再需要线程局部变量时,调用ThreadLocal.remove()方法显式地从当前线程的ThreadLocalMap中移除该变量。
  2. 使用ThreadLocal的适当生命周期:确保线程局部变量的生命周期不超过需要的范围。例如,在请求处理完成后清理线程局部变量。
  3. 避免静态ThreadLocal变量:除非绝对必要,否则避免使用静态的ThreadLocal变量,因为它们的生命周期更长。
  4. 线程池配置:对于线程池中的线程,在线程空闲或回收时,可以考虑清理线程局部变量。
    以下是一个正确的ThreadLocal使用示例,展示了如何避免内存泄漏:
public class ThreadLocalExample {private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 设置线程局部变量threadLocal.set("ThreadLocal value");// 使用线程局部变量System.out.println("Value from threadLocal: " + threadLocal.get());// 显式释放线程局部变量threadLocal.remove();}
}

ThreadLocalMap的哈希机制

ThreadLocalMap使用哈希表实现,其哈希机制直接影响着线程局部变量的存取效率。

哈希函数的选择与实现

ThreadLocalMap使用对象的hashCode()作为哈希函数,这是一个简单而常见的做法。具体实现如下:

int h = key.hashCode() & (len - 1);

这个表达式通过按位与操作(len-1)将哈希码映射到哈希表的索引范围内。这种方式简单高效,而且能均匀分布哈希值。

哈希冲突的处理机制

哈希冲突是不可避免的,当两个不同的键计算出相同的哈希码时,ThreadLocalMap使用链表法(Separate Chaining)来处理冲突。每个哈希表槽(bucket)实际上是一个链表的头节点,所有具有相同哈希码的条目都链接在这个链表中。

private void set(ThreadLocal<?> key, Object value) {Entry[] tab = table;int len = tab.length;//threadLocalHashCode计算:每创建一个ThreadLocal对象就新增一个黄金分割数,哈希码分布非常均匀int i = key.threadLocalHashCode & (len - 1);// 向后探测空槽或相同keyfor (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {//nextIndex未处理哈希冲突的关键ThreadLocal<?> k = e.get();if (k == key) {e.value = value;  // 更新旧值return;}if (k == null) {// 清理过期key后插入新值replaceStaleEntry(key, value, i);return;}}// 找到空位后插入tab[i] = new Entry(key, value);size++;// 如果超出阈值,扩容if (size >= threshold)resize();
}

这段代码遍历链表,查找与给定键相同的条目。如果找到匹配的条目,则更新其值;如果未找到匹配的条目,则将新条目插入链表头部。

ThreadLocalMap的扩容机制

随着线程局部变量的增加,ThreadLocalMap需要动态调整其容量以维持高效的存取操作。这涉及负载因子和扩容策略的设计。
默认 threshold 为2/3size,当前size>threshold3/4后进行扩容。当前size> threshold会rehash(清理null-key)。

扩容过程:

  1. 先清理后扩容:先会清理整个数组,将key为null的回收。然后再创建一个新数组,大小为原先的2倍。
  2. 重新哈希所有条目:将旧哈希表中的所有有效条目重新计算哈希码,并插入到新哈希表中。(线性探测法寻找空位)
private void resize() {Entry[] oldTab = table;int oldLen = oldTab.length;int newLen = oldLen * 2; // 容量翻倍Entry[] newTab = new Entry[newLen];int count = 0;for (Entry e : oldTab) {if (e != null) {ThreadLocal<?> k = e.get();if (k == null) {// Entry 已失效,value 清理e.value = null;} else {// 重新计算索引位置int h = k.threadLocalHashCode & (newLen - 1);while (newTab[h] != null)h = nextIndex(h, newLen); // 开放寻址法处理哈希冲突newTab[h] = e;count++;}}}// 设置阈值setThreshold(newLen);//newlen*2/3size = count;table = newTab;
}

从这段代码可以看出,扩容过程通过线性探测(Linear Probing)来解决新哈希表中的冲突。当哈希冲突发生时,它会查找下一个可用的槽,直到找到一个空槽为止。

线程安全机制

ThreadLocal的设计充分考虑了线程安全问题,通过多种机制确保其在多线程环境下的正确性。

与线程生命周期的协调

ThreadLocal的线程安全不仅限于哈希表操作的同步,还包括与线程生命周期的协调。当线程结束时,其ThreadLocalMap应该被清理,以释放所有线程局部变量。
然而,由于线程结束是由Java虚拟机自动处理的,ThreadLocal需要确保当线程结束时,其线程局部变量不会导致内存泄漏。这正是使用弱引用的主要原因。
人话:你不手动 remove() 的话,至少 GC 能清除 “key 为 null 的 entry”

否则,value(即线程私有变量)可能会“活在 map 里”无法访问,也无法回收 → 内存泄漏。当然最好还是直接remove就行了。

性能考量与权衡

使用ThreadLocal时需要考虑以下性能因素:

  1. 哈希表操作的开销:线程局部变量的存取涉及哈希计算和可能的链表遍历,这会带来一定的性能开销。
  2. 线程上下文切换:频繁切换线程会导致线程局部变量的缓存效率下降,因为每次切换线程时,都需要访问不同的ThreadLocalMap。
  3. 内存占用:每个线程维护自己的ThreadLocalMap,这会增加内存占用,特别是当线程数量较多时。
  4. 缺乏自动清理机制:缺乏自动清理机制需要手动调用remove(),否则容易出问题。
    ThreadLocal改进方案
    ThreadLocal 是 Java 中非常实用的线程上下文工具,但其本身存在一些局限性,比如:
  • 内存泄漏风险
  • 线程复用问题(如在线程池中使用 ThreadLocal)
  • 父子线程间数据无法传递
  • 缺乏统一管理机制

为了解决这些问题,社区和官方都提出了一些改进方案。下面我将详细介绍这些改进方案,并对比它们的优缺点、适用场景以及源码设计思想。


🧠 一、ThreadLocal 的主要问题总结

问题描述
内存泄漏Entry 的 key 是弱引用,value 是强引用,不及时清理会导致内存泄漏
线程复用问题在线程池中,ThreadLocal 变量可能被后续任务错误读取
不支持父子线程传值子线程无法继承父线程的 ThreadLocal 值
缺乏自动清理机制需要手动调用 remove(),否则容易出问题

二、改进方案详解

方案一:InheritableThreadLocal

Java 原生提供的子类,允许子线程继承父线程的 ThreadLocal 值。

使用方式:

ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();

实现原理:

  • 在创建子线程时,JVM 会拷贝父线程的 ThreadLocalMap 到子线程。
  • 拷贝过程在 Thread.init() 方法中完成。

缺点:

  • 只能在显式创建子线程时生效,对线程池无效;
  • 不能动态更新父线程变量后影响子线程
  • 仍然存在内存泄漏问题

方案二:TransmittableThreadLocal(阿里开源)

介绍:

由 Alibaba 开源的增强版 ThreadLocal,用于解决 线程池中 ThreadLocal 值传递问题,是目前最主流的解决方案之一。

GitHub 地址:TransmittableThreadLocal

特点:

  • 支持线程池中父子线程的上下文传递;(子进程可以继承父进程的功能)
  • 提供封装工具类,可适配 ExecutorServiceForkJoinPoolCompletableFuture
  • 支持自定义传递策略;
  • 支持自动清理资源。

使用示例:

TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();context.set("hello");Runnable task = () -> System.out.println(context.get());// 包装任务以支持上下文传递
ExecutorService executor = Executors.newFixedThreadPool(1);
executor.submit(TtlRunnable.get(task));

实现原理:

  • 利用装饰器模式,包装 Runnable / Callable,在执行前后保存/恢复上下文;
  • 内部维护一个 CopyOnWriteMap,记录当前线程的 TtlThreadLocal 值;
  • 在任务提交前复制上下文,在任务执行完成后恢复原上下文。

优势:

  • 完美兼容线程池、异步任务(CompletableFuture)、ForkJoin;
  • 社区活跃,文档丰富,已广泛用于 Dubbo、SkyWalking、RocketMQ 等项目。

方案三:FastThreadLocal(Netty 实现)

介绍:

Netty 自研的高性能 ThreadLocal 替代方案,性能优于 JDK 原生实现。

特点:

  • 更快的 get/set 操作(基于数组索引访问);
  • 更少的 GC 压力;
  • 更好的并发性能;
  • 可与 Netty 的 EventLoop 紧密结合。

使用方式:

FastThreadLocal<String> fastThreadLocal = new FastThreadLocal<>();fastThreadLocal.set("netty");
System.out.println(fastThreadLocal.get());

实现原理:

  • 每个 FastThreadLocal 对应一个唯一的 index;
  • 每个线程内部维护一个 Object[] 数组,通过 index 直接访问;
  • 无哈希冲突、无扩容机制,效率更高。

限制:

  • 必须使用 Netty 的 FastThreadLocalThreadFastThreadLocalRunnable

  • 不支持线程池自动传递(需自行封装);

方案四:RequestAttributes + RequestContextHolder(Spring 封装)

Spring 提供的请求上下文管理类,底层基于 ThreadLocal,用于 Web 请求中存储用户信息、Locale、Session 等。

使用方式:

RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
requestAttributes.setAttribute("user", user, RequestAttributes.SCOPE_REQUEST);

实现原理:

  • 底层使用 RequestAttributes 接口和 ServletRequestAttributes 实现;
  • 结合 Spring 的拦截器或过滤器,在请求开始时设置上下文,结束时清除;
  • 多用于 Controller、AOP、Filter 层获取请求信息。

优势:

  • 专为 Web 场景设计,集成度高;
  • 自动处理生命周期,避免内存泄漏;
  • 支持 request/session scope。

限制:

  • 仅适用于 Web 项目;
  • 不能跨线程传递上下文(需要配合其他方案);

方案五:使用 ThreadLocal + AOP + Filter 统一管理

在非 Spring Boot 项目或自定义框架中,可以通过以下方式手动管理 ThreadLocal 生命周期:

  • 在 Filter 中初始化上下文;
  • 在 AOP 或 Interceptor 中设置数据;
  • 在最后阶段自动 remove 数据;
  • 使用线程池时,结合 TtlRunnable 手动包装任务。

优点:

  • 灵活可控;
  • 可根据业务需求定制;
  • 可组合使用多种改进方案。
场景推荐方案
Web 请求上下文管理RequestContextHolder
微服务链路追踪TransmittableThreadLocal
异步任务、CompletableFutureTransmittableThreadLocal
Netty 网络通信FastThreadLocal
自定义框架ThreadLocal + AOP/Filter + 手动清理
跨线程传值TransmittableThreadLocal 或手动封装

感谢你看到这里,喜欢的可以点点关注哦!

相关文章:

ThreadLocal源码深度剖析:内存管理与哈希机制

ThreadLocal是Java并发编程中的重要工具&#xff0c;它为每个线程提供独立的变量存储空间&#xff0c;实现了线程之间的数据隔离。本文将从源码实现角度&#xff0c;深入分析ThreadLocal的内部机制&#xff0c;特别是强弱引用关系、内存泄漏问题、ThreadLocalMap的扩容机制以及…...

Softmax回归与单层感知机对比

(1) 输出形式 Softmax回归 输出是一个概率分布&#xff0c;通过Softmax函数将线性得分转换为概率&#xff1a; 其中 KK 是类别数&#xff0c;模型同时计算所有类别的概率。 单层感知机 输出是二分类的硬决策&#xff08;如0/1或1&#xff09;&#xff1a; 无概率解释&#x…...

数字社会学家唐兴通谈数字行动主义网络行动主义与标签行动主义,理解它才算抓住AI社会学与网络社会学关键所在

让我们继续探讨一个在数字时代至关重要的概念——数字行动主义&#xff08;Digital Activism&#xff09;、网络行动主义&#xff08;Cyberactivism&#xff09;以及标签行动主义&#xff08;Hashtag Activism&#xff09;。我将尽力从一个数字社会学家的角度&#xff0c;抽丝剥…...

PandasAI:对话式数据分析新时代

PandasAI&#xff1a;对话式数据分析新时代 引言项目概述分析基本信息 核心功能详解1. 自然语言查询处理2. 数据可视化生成3. 多数据源集成分析4. 安全沙箱执行5. 云平台协作功能 安装和使用教程1.环境要求2.安装步骤3.基本使用方法4.切换其他LLM 应用场景和实际价值1.适用业务…...

全球化电商平台AWS云架构设计

业务需求&#xff1a; 支撑全球三大区域&#xff08;北美/欧洲/亚洲&#xff09;用户访问&#xff0c;延迟<100ms处理每秒50,000订单的峰值流量混合云架构整合本地ERP系统全年可用性99.99%满足GDPR和PCI DSS合规要求 以下是一个体现AWS专家能力的全球化电商平台架构设计方…...

Linux 怎么使用局域网内电脑的网络访问外部

一次性 export http_proxy"http://192.168.0.188:7890" export https_proxy"http://192.168.0.188:7890"一直生效 写入 ~/.bashrc&#xff08;或 ~/.bash_profile&#xff09; nano ~/.bashrc加入这一行&#xff1a; export http_proxy"http://19…...

Python-numpy中ndarray对象创建,数据类型,基本属性

numpy库 numpy中的数据结构ndarrayndarray中的dtypendarray中的dtype的指定方式创建ndarray及指定dtype从列表创建ndarray使用 np.empty(), np.zeros(), np.ones() 和 np.full() 创建特定值的数组使用 np.arange() 创建等差数列数组使用 np.linspace() 创建等差数组使用np.logs…...

Python从入门到高手8.2节-元组的常用操作符

目录 ​8.2.1 元组的常用操作符 8.2.2 []操作符: 索引访问元组 8.2.3 [:]操作符&#xff1a;元组的切片 8.2.4 操作符&#xff1a;元组的加法 8.2.5 *操作符&#xff1a;元组的乘法 8.2.6 元组的关系运算 8.2.7 in操作符&#xff1a;查找元素 8.2.8 五一她玩了个狗吃…...

Python内置函数

Python作为一门简洁强大的编程语言&#xff0c;提供了丰富的内置函数&#xff08;Built-in Functions&#xff09;&#xff0c;这些函数无需导入任何模块即可直接使用。本文将介绍Python中最常用、最重要的内置函数&#xff0c;帮助初学者快速掌握这些强大的工具。 官方地址&a…...

一款基于 .NET 开源的多功能的 B 站视频下载工具

前言 哔哩哔哩&#xff08;B站&#xff09;是一个知名的视频学习平台&#xff0c;作为程序员而言这是一个非常值得推荐的网站。今天大姚给大家推荐一款基于 .NET 开源的多功能的 B 站视频下载工具&#xff1a;downkyi。 项目介绍 downkyi&#xff08;哔哩下载姬&#xff09;…...

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】5.2 数据分组与透视(CUBE/ROLLUP/GROUPING SETS)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 5.2 数据分组与透视&#xff1a;CUBE/ROLLUP/GROUPING SETS深度解析5.2.1 数据准备与分析目标数据集与表结构分析目标 5.2.2 ROLLUP&#xff1a;层级化分组汇总功能与语法示…...

20、数据可视化:魔镜报表——React 19 图表集成

一、魔镜的预言本质 "数据可视化是霍格沃茨的预言水晶球&#xff0c;将混沌的数据星尘转化为可解读的命运轨迹&#xff01;" 魔法部占卜司官员挥舞魔杖&#xff0c;Echarts与Three.js的图表矩阵在空中交织成动态星图。 ——基于《国际魔法联合会》第9号可视化协议&a…...

笔记本电脑升级计划(2017———2025)

ThinkPad T470 (2017) vs ThinkBook 16 (2025) 完整性能对比报告 一、核心硬件性能对比 1. CPU性能对比&#xff08;i5-7200U vs Ultra9-285H&#xff09; 参数i5-7200U (2017)Ultra9-285H (2025)提升百分比核心架构2核4线程 (Skylake)16核16线程 (6P8E2LPE)700%核心数制程工…...

Flutter——数据库Drift开发详细教程(四)

目录 参考正文表达式1.比较2.布尔代数3.算术BigIn 4.空值检查6.日期和时间7.IN和NOT IN8.聚合函数&#xff08;例如 count 和 sum&#xff09;8.1比较8.2算术8.4计数8.5group_concat8.9窗口函数 9.数学函数和正则表达式10.子查询10.1 标量子查询10.2 isInQuery10.3 存在10.4完整…...

android-ndk开发(6): 查看反汇编

android-ndk开发(6): 查看反汇编 2025/05/05 1. 概要 android-ndk 是基于 clang 的工具链&#xff0c; clang 则保持了和 gcc 的高度兼容。 在 Linux 开发机上&#xff0c; GCC 套件里的 objdump 提供了反汇编的功能。 实际上 android-ndk 也提供了一份 objdump&#xff0c;…...

浅析AI大模型为何需要向量数据库?【入门基础】

文章目录 引言&#xff1a;大模型时代的存储挑战一、向量数据库&#xff1a;大模型的"海马体"1.1 什么是向量数据库&#xff1f;1.2 为什么大模型离不开向量数据库&#xff1f;(1) 嵌入(Embedding)的本质(2) 突破上下文窗口限制 二、相似性度量&#xff1a;欧氏距离与…...

Java面试:微服务与大数据场景下的技术挑战

面试对话场景 第一轮&#xff1a;基础知识考察 面试官&#xff1a;谢先生&#xff0c;您能简单介绍一下Java SE 8的新特性吗&#xff1f; 谢飞机&#xff1a;当然&#xff0c;Java SE 8引入了Lambda表达式、Stream API和新的日期时间API&#xff0c;大大简化了代码编写。 面…...

[前端]异步请求的竞态问题

竞态条件简介 遇到的问题 切换标签请求数据&#xff0c;但又快速切换标签请求数据&#xff0c;展示的是前一个标签的数据&#xff0c; 需要在切换标签时添加取消请求的机制&#xff0c;使用AbortController来取消正在进行的请求。当用户快速切换标签时&#xff0c;取消之前的请…...

【PDF拆分+提取内容改名】批量拆分PDF提取拆分后的每个PDF物流面单数据改名或导出表格,基于WPF的PDF物流面单批量处理方案

应用场景 物流行业每天需要处理大量包含物流面单的PDF文件,这些文件通常包含运单号、收发货人信息、货物详情等重要数据。传统手动处理方式效率低下且容易出错。本方案通过WPF实现一个自动化工具,能够: 批量拆分多页PDF为单页文件提取每页面单中的关键信息(如运单号、收件人…...

adb无线调试步骤

环境&#xff1a; macOS&#xff1b; 换成 linux 或 windows 也支持的小米15 Pro&#xff1b; 换成其他 android 手机也支持的 电脑和手机接入相同Wifi在电脑上&#xff0c;确保安装了 adb 对于 Android 开发者&#xff0c; 一般是是通过 Android Studio 安装对于 ndk 开发者…...

RocketMQ与Kafka的区别

文章目录 相同之处不同之处存储形式性能对比传输系统调用存储可靠性单机支持的队列数延时消息消息重复消息过滤消息失败重试死信队列 DLQ回溯消息分布式事务服务发现开发语言友好性开源社区活跃度商业支持成熟度 总结Kafka 和 RocketMQ 怎么选&#xff1f; 本文参考&#xff1a…...

剥开 MP4 的 千层 “数字洋葱”:从外到内拆解通用媒体容器的核心

在当今数字化时代&#xff0c;MP4 格式随处可见&#xff0c;无论是在线视频、手机拍摄的短片&#xff0c;还是从各种渠道获取的音频视频文件&#xff0c;MP4 都占据着主流地位。它就像一个万能的 “数字媒体集装箱”&#xff0c;高效地整合和传输着各种视听内容。接下来&#x…...

设计模式(结构型)-组合模式

定义 组合模式的定义为&#xff1a;将对象组合成树形结构以表示 “部分 - 整体” 的层次结构&#xff0c;并且使得用户对单个对象和组合对象的使用具有一致性。其最关键的实现要点在于&#xff0c;简单对象和复合对象必须实现相同的接口&#xff0c;这一特性正是组合模式能够对…...

使用 IDEA + Maven 搭建传统 Spring MVC + Thymeleaf 项目的详细步骤

使用 IDEA Maven 搭建传统 Spring MVC Thymeleaf 项目 环境准备步骤 1&#xff1a;创建 Maven 项目步骤 2&#xff1a;添加依赖&#xff08;pom.xml&#xff09;步骤 3&#xff1a;配置 web.xml步骤 4&#xff1a;Spring 配置类&#xff08;Java Config&#xff09;步骤 5&am…...

「Mac畅玩AIGC与多模态19」开发篇15 - 判断节点与工具节点联动示例

一、概述 本篇在引入工具节点的基础上&#xff0c;进一步结合判断节点&#xff08;条件分支&#xff09;&#xff0c;实现根据用户输入内容动态控制是否调用外部接口。通过构建“用户是否需要天气信息”的条件逻辑&#xff0c;开发人员将掌握如何在 Dify 工作流中通过条件判断…...

docker 外部能访问外网,内部不行(代理问题)

如果宿主机访问外网依赖代理&#xff08;比如 http_proxy 环境变量&#xff09;&#xff0c;容器默认不会继承。需要显式传入代理&#xff1a; docker run -e http_proxy... -e https_proxy... ...在 docker-compose 中配置 HTTP/HTTPS 代理 version: 3 services:app:image: …...

模糊控制理论(含仿真)

本文讲解模糊控制理论、设计步骤以及案例。 1. 模糊控制原理&#xff1a; 模糊控制&#xff08;Fuzzy Control&#xff09;是一种基于模糊逻辑推理的人类经验规则实现的控制方法&#xff0c;适用于对系统模型不精确或难以建立精确数学模型的复杂系统。它利用“如果…那么…”&…...

《 C++ 点滴漫谈: 三十六 》lambda表达式

一、引言 在 C98 和 C03 时代&#xff0c;尽管 C 拥有强大的泛型编程能力和丰富的面向对象特性&#xff0c;但在表达局部逻辑、回调行为或一次性函数处理时&#xff0c;程序员却常常需要冗长的代码来定义函数对象&#xff08;functor&#xff09;&#xff0c;或者使用函数指针…...

【C/C++】函数模板

&#x1f3af; C 学习笔记&#xff1a;函数模板&#xff08;Function Template&#xff09; 本文是面向 C 初学者的函数模板学习笔记&#xff0c;内容包括基本概念、定义与使用、实例化过程、注意事项等&#xff0c;附带示例代码&#xff0c;便于理解与复现。 &#x1f4cc; 一…...

电赛经验分享——模块篇

1、前言 打算在这一个专栏中&#xff0c;分享一些本科控制题电赛期间的经验&#xff0c;和大家共同探讨&#xff0c;也希望能帮助刚刚参加电赛的同学&#xff0c;了解一些基本的知识。一些见解和看法可能不同或有错误&#xff0c;欢迎批评指正。 在本文中&#xff0c;主要介绍笔…...

LeetCode 热题 100 70. 爬楼梯

LeetCode 热题 100 | 70. 爬楼梯 大家好&#xff0c;今天我们来解决一道经典的动态规划入门题——爬楼梯。这道题在LeetCode上被标记为简单难度&#xff0c;要求我们计算爬到第n阶楼梯的不同方法数&#xff0c;每次可以爬1或2个台阶。下面我将详细讲解解题思路&#xff0c;并附…...

浔川AI测试版内测报告

浔川AI测试版内测报告 一、引言 本次对浔川AI测试版进行内测&#xff0c;旨在全面评估其功能表现与性能状况&#xff0c;为后续的优化升级及正式上线提供有力依据。 二、测试环境 1. 硬件环境&#xff1a;[Windows 10】 2. 软件环境&#xff1a;操作系统为【核桃编程]&#xff…...

Leetcode刷题记录31——旋转图像

题源&#xff1a;https://leetcode.cn/problems/rotate-image/description/?envTypestudy-plan-v2&envIdtop-100-liked 题目描述&#xff1a; 思路一&#xff1a; &#x1f4a1; 解题思路&#xff1a;分两步完成旋转 虽然“直接旋转”看起来有点抽象&#xff0c;但我们…...

攻防世界-php伪协议和文件包含

fileinclude 可以看到正常回显里面显示lan参数有cookie值表示为language 然后进行一个判断&#xff0c;如果参数不是等于英语&#xff0c;就加上.php&#xff0c;那我们就可以在前面进行注入一个参数&#xff0c;即flag&#xff0c; payload&#xff1a;COOKIE:languageflag …...

[C++] 小游戏 决战苍穹

大家好&#xff0c;各位看到这个标题&#xff0c;斗破苍穹什么时候改叫决战苍穹了&#xff1f;其实&#xff0c;因为版权等一系列问题&#xff0c;斗破苍穹正式改名为决战苍穹&#xff0c;这个版本主要更新内容为解决了皇冠竞技场太过影响游戏平衡&#xff0c;并且提高了一些装…...

项目成本管理_挣得进度ES

在项目成本管理的新实践中&#xff0c; 通过挣值管理(EVM) 的扩展&#xff0c;引入 挣得进度ES 这一概念&#xff0c; ES是EVM理论和实践的延伸&#xff0c;挣得进度理论用ES和实际时间(AT) 替代了传统EVM所使用的进度偏差测量指标SV(挣值—计划价值)。 使用这种替代方法计算…...

矩阵快速幂 快速求解递推公式

文章目录 习题790.多米诺和托米诺平铺 对于一个给定的递推公式&#xff0c;例如dp[i] dp[i-1] * a dp[i-2] * b,那么常用的做法&#xff0c;肯定是使用o(n)的时间复杂度进行线性求解&#xff0c;但是如果 n 10 18 n{10}^{18} n1018&#xff0c;那么肯定超时的&#xff0c;这…...

驱动开发硬核特训 · Day 28(上篇):pinctrl 子系统详解与实战分析

&#x1f4c5; 日期&#xff1a;2025-05-05 &#x1f4da; 技术平台&#xff1a;嵌入式Jerry&#xff08;B站&#xff09; 一、引言 在嵌入式系统中&#xff0c;SoC 芯片的引脚通常具有多种功能&#xff0c;如 GPIO、UART、I2C、SPI 等。为了在不同的应用场景中灵活配置引脚功…...

20250505下载VLC for Android

20250505下载VLC for Android 2025/5/5 14:35 缘起&#xff1a;做Rockchip的RK3566的Android13下的跨网段PING。 酷芯的图传网段 和 softAP/以太网RJ45共享网段之间互相PING通。 图传的原厂/供应商说可以使用ffmpeg进行rtsp流的转发。 后来确认VLC for Android版本只有接受流&a…...

Jetpack Compose 响应式布局实战:BoxWithConstraints 完全指南

深入理解 Jetpack Compose 中的 BoxWithConstraints 前言 在构建现代 Android 应用时&#xff0c;响应式设计已成为必不可少的要求。Jetpack Compose 作为 Android 的现代 UI 工具包&#xff0c;提供了 BoxWithConstraints 这一强大组件&#xff0c;帮助我们轻松创建能够适应…...

ZYNQ笔记(十七):IP核封装与接口定义

版本&#xff1a;Vivado2020.2&#xff08;Vitis&#xff09; 任务&#xff1a;将“HDMI彩条显示实验”&#xff08;正点原子 ZYNQ FPGA 开发视频&#xff09;中所实现的 RGB2DVI 模块封装成一个 IP 核。 目录 一、介绍 &#xff08;1&#xff09;IP核 &#xff08;2&#x…...

学习笔记msp430f5529lp

注&#xff1a;本文仅用于个人学习使用&#xff0c;记录笔记。 学习视频msp430f5529库函数入门教程 00.序言_哔哩哔哩_bilibili 向大佬致敬理工男小帅-CSDN博客 CCS环境快捷键使用 代码注释&#xff1a;Ctrl/ 提示/补全: CtrlShiftC 放大:Ctrl 缩小:Ctrl- 切换选择模式&…...

人工智能应用:从技术突破到生态重构的演进之路

一、人工智能的发展历程&#xff1a;从符号主义到通用智能探索 人工智能&#xff08;AI&#xff09;的发展始于20世纪中叶&#xff0c;其历程可划分为四个关键阶段&#xff1a; ​符号主义与早期探索&#xff08;1950s-1970s&#xff09;​​ 以逻辑推理和专家系统为核心&…...

【ZYNQ Linux移植】4-内核移植

文章目录 0 写在前面1 内核源码的文件结构2 Linux内核移植2.1 移植配置文件2.2 移植设备树2.3 创建脚本进行编译2.4 备份相关文件 3 测试4 总结5 参考资料 0 写在前面 这是一个系列博客&#xff0c;详细介绍如何在 ZYNQ 与 ZYNQ MP 平台上如何移植 Linux 系统。目前网络上的大部…...

代码随想录算法训练营第三十二天

LeetCode/卡码网题目: 518. 零钱兑换 II377. 组合总和 Ⅳ790. 多米诺和托米诺平铺(每日一题)57. 爬楼梯&#xff08;第八期模拟笔试&#xff09; 其他: 今日总结 往期打卡 背包问题特点: 滚动数组背包遍历顺序 完全背包从小到大,即基于当前物品更新过的继续更新01背包从大到…...

java CompletableFuture 异步编程工具用法1

1、测试异步调用&#xff1a; static void testCompletableFuture1() throws ExecutionException, InterruptedException {// 1、无返回值的异步任务。异步线程执行RunnableCompletableFuture.runAsync(() -> System.out.println("only you"));// 2、有返回值的异…...

Spring Boot 集成 Solr 的详细步骤及示例

环境准备 安装 Solr &#xff1a;从 Solr 官网&#xff08;Welcome to Apache Solr - Apache Solr&#xff09;下载并安装最新版本&#xff0c;然后通过命令 bin/solr start 启动 Solr 服务&#xff0c;使用 bin/solr create -c mycore 创建一个新的 Solr 核心。 安装 JDK &am…...

Nemotron-Research-Tool-N1 如何提升大语言模型工具使用能力?

Nemotron-Research-Tool-N1如何提升大语言模型工具使用能力&#xff1f; 如今&#xff0c;大语言模型&#xff08;LLMs&#xff09;发展迅猛&#xff0c;给它配备外部工具成为研究热点。但传统方法存在不少问题。这篇论文提出的Nemotron-Research-Tool-N1系列模型带来新突破&a…...

OpenCV进阶操作:图像直方图、直方图均衡化

文章目录 一、图像直方图二、图像直方图的作用三、使用matplotlib方法绘制直方图2.使用opencv的方法绘制直方图&#xff08;划分16个小的子亮度区间&#xff09;3、绘制彩色图像的直方图 四、直方图均衡化1、绘制原图的直方图2、绘制经过直方图均衡化后的图片的直方图3、自适应…...

Android控件VideoView用法

一 控件UI <VideoViewandroid:id="@+id/videoView"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="fitCenter" /> 二 配置 <?xml version="1.0" encoding="u…...