ThreadLocal - 原理与应用场景详解
ThreadLocal 的基础概念
在 Java 的多线程世界里,线程之间的数据共享与隔离一直是一个关键话题。如果处理不当,很容易引发线程安全问题,比如数据混乱、脏读等。而 ThreadLocal
这个工具类,就像是为线程量身定制的 “私人储物柜”,为每个线程提供了独立的存储空间,完美地解决了线程间数据隔离的问题。
ThreadLocal 是什么?
ThreadLocal
是 Java 中一个非常实用的类,它为每个线程都提供了自己独立的变量副本。换句话说,每个线程都可以通过 ThreadLocal
来设置(set
)和获取(get
)自己的私有变量,而不会和其他线程产生任何干扰,就像每个线程都有自己的 “小金库”,互不干扰,互不影响。
举个简单的例子,假如我们有一个变量 count
,在普通情况下,多个线程同时访问这个变量时,很容易出现数据混乱的情况,因为它们都操作的是同一个内存地址的变量。但如果我们把 count
放到 ThreadLocal
中,那么每个线程都会有自己独立的 count
副本,线程 A 对它的 count
副本进行修改,完全不会影响到线程 B 的 count
副本。
ThreadLocal 的基本功能与特点
- 线程隔离 :这是
ThreadLocal
最显著的特点。每个线程对ThreadLocal
变量的读写操作都局限在自己的线程内,完全不会与其他线程产生数据共享或冲突。这种线程隔离的特性使得ThreadLocal
在处理一些需要线程私有数据的场景时非常有用,比如在每个线程中保存独立的配置信息、用户身份信息等。 - 无需显式加锁 :由于线程间的数据隔离,使用
ThreadLocal
变量时,不需要像操作共享变量那样使用显式的锁机制(如synchronized
或ReentrantLock
)来保证线程安全。这大大简化了多线程编程的复杂度,提高了开发效率。
ThreadLocal 的基础使用示例
下面通过一个简单的代码示例来感受一下 ThreadLocal
的基本使用方式:
public class ThreadLocalExample {// 创建一个ThreadLocal变量private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 在主线程中设置ThreadLocal变量的值threadLocal.set("主线程的值");// 在主线程中获取ThreadLocal变量的值System.out.println("主线程获取的值:" + threadLocal.get());// 启动两个子线程for (int i = 0; i < 2; i++) {new Thread(() -> {// 子线程设置自己的ThreadLocal变量的值threadLocal.set(Thread.currentThread().getName() + "的值");// 子线程获取自己的ThreadLocal变量的值System.out.println(Thread.currentThread().getName() + "获取的值:" + threadLocal.get());// 子线程结束后清理ThreadLocal变量threadLocal.remove();}).start();}// 主线程结束后清理ThreadLocal变量threadLocal.remove();}
}
代码运行结果示例 :
主线程获取的值:主线程的值
Thread-0获取的值:Thread-0的值
Thread-1获取的值:Thread-1的值
应用场景概览
ThreadLocal
在实际开发中有着广泛的应用场景,以下是一些常见的场景:
1. 线程隔离
在线程池或者其他多线程场景中,我们可以用 ThreadLocal
来存储每个线程的独立数据,从而避免多线程共享数据带来的问题。例如,存储每个线程的日志信息、用户身份信息等。
public class UserContextHolder {private static final ThreadLocal<User> userThreadLocal = new ThreadLocal<>();public static void setUser(User user) {userThreadLocal.set(user);}public static User getUser() {return userThreadLocal.get();}public static void removeUser() {userThreadLocal.remove();}
}// 在线程中使用
public class MyRunnable implements Runnable {@Overridepublic void run() {User user = new User("User-" + Thread.currentThread().getName());UserContextHolder.setUser(user);// 执行业务逻辑System.out.println("当前线程:" + Thread.currentThread().getName() + ",用户:" + UserContextHolder.getUser().getName());UserContextHolder.removeUser();}
}
场景模拟: 假设我们有一个在线教育平台,不同的线程代表不同的用户请求。我们可以通过 ThreadLocal
存储每个用户的身份信息,这样在后续的业务逻辑处理中,就可以方便地获取当前用户的信息,而不会和其他线程的用户信息混在一起。
2. 跨层数据传递
在分层架构的系统中,ThreadLocal
可以用来在不同的层之间传递数据,而无需在每一层都显式地传递参数。例如,在 Web 开发中,从控制器层到服务层再到数据访问层,传递请求相关的数据。
public class RequestContextHolder {private static final ThreadLocal<RequestData> requestDataThreadLocal = new ThreadLocal<>();public static void setRequestData(RequestData requestData) {requestDataThreadLocal.set(requestData);}public static RequestData getRequestData() {return requestDataThreadLocal.get();}public static void removeRequestData() {requestDataThreadLocal.remove();}
}// 控制器层
@RestController
@RequestMapping("/api")
public class MyController {@PostMapping("/process")public String processRequest(@RequestBody RequestData requestData) {RequestContextHolder.setRequestData(requestData);// 调用服务层myService.process();RequestContextHolder.removeRequestData();return "Request processed successfully";}
}// 服务层
@Service
public class MyService {public void process() {RequestData requestData = RequestContextHolder.getRequestData();// 使用 requestData 进行业务处理System.out.println("Processing request: " + requestData);}
}
场景模拟: 在处理一个 HTTP 请求时,我们可以在控制器层将请求的相关数据(如请求 ID、用户身份信息等)存储到 ThreadLocal
中。然后在服务层和数据访问层,就可以直接从 ThreadLocal
中获取这些数据,而无需在每一层都显式地传递参数。这大大简化了代码逻辑,提高了开发效率。
3. 复杂调用链路的全局参数传递
在复杂的调用链路中,比如分布式系统中的请求跟踪、日志记录等场景,ThreadLocal
可以用来在整个调用链中保持某些参数的连续性。
public class TraceContextHolder {private static final ThreadLocal<String> traceIdThreadLocal = new ThreadLocal<>();public static void setTraceId(String traceId) {traceIdThreadLocal.set(traceId);}public static String getTraceId() {return traceIdThreadLocal.get();}public static void removeTraceId() {traceIdThreadLocal.remove();}
}// 在入口处设置 Trace ID
public class ApiGatewayFilter implements GenericFilterBean {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {String traceId = UUID.randomUUID().toString();TraceContextHolder.setTraceId(traceId);try {chain.doFilter(request, response);} finally {TraceContextHolder.removeTraceId();}}
}// 在后续的服务调用中使用 Trace ID
public class MyService {public void process() {String traceId = TraceContextHolder.getTraceId();// 使用 traceId 进行日志记录等操作System.out.println("Processing with traceId: " + traceId);}
}
场景模拟: 在一个分布式系统中,当一个请求进入系统时,我们在入口处(如 API 网关)生成一个唯一的 Trace ID,并将其存储到 ThreadLocal
中。在后续的各个服务调用中,都可以从 ThreadLocal
中获取这个 Trace ID,用于日志记录、请求跟踪等操作。这样可以方便地追踪一个请求在整个系统中的流转路径,便于问题排查和性能分析。
4. 数据库连接的管理
在涉及到数据库连接的嵌套调用场景中,ThreadLocal
可以用来确保每个线程都有自己的数据库连接,避免连接共享带来的问题,保证事务的一致性。
public class DBContextHolder {private static final ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<>();public static void setConnection(Connection connection) {connectionThreadLocal.set(connection);}public static Connection getConnection() {return connectionThreadLocal.get();}public static void removeConnection() {connectionThreadLocal.remove();}
}// 在数据访问层获取数据库连接
public class MyDAO {public void executeQuery(String sql) {Connection connection = null;try {connection = DBContextHolder.getConnection();if (connection == null) {connection = dataSource.getConnection();DBContextHolder.setConnection(connection);}// 执行 SQL 查询System.out.println("Executing query: " + sql + " on connection: " + connection.hashCode());} catch (SQLException e) {e.printStackTrace();} finally {// 在实际开发中,连接的关闭可能需要根据具体情况处理// DBContextHolder.removeConnection();}}
}
场景模拟: 在 AOP(面向切面编程)场景中,当我们进行数据库操作时,可以通过 ThreadLocal
来管理数据库连接。在事务的开始阶段,获取一个数据库连接并存储到 ThreadLocal
中。在后续的多个数据库操作中,都可以从 ThreadLocal
中获取这个连接,确保所有的操作都在同一个数据库连接上执行,从而保证事务的一致性。
总结
ThreadLocal
的应用场景非常丰富,它在实现线程隔离、跨层数据传递、复杂调用链路的全局参数传递以及数据库连接管理等方面都有着独特的价值。通过这些实际的应用场景,我们可以看到 ThreadLocal
在简化多线程编程复杂度、提高代码可维护性方面的重要作用。在接下来的章节中,我们将深入探讨 ThreadLocal
的工作原理,进一步加深对其的理解。
以上是 ThreadLocal
的应用场景概览,希望这些内容能帮助你更好地理解和使用 ThreadLocal
。如果你有任何问题或想法,欢迎随时交流!
ThreadLocal 的原理剖析
了解了 ThreadLocal
的应用场景后,现在我们来深入探讨一下它的工作原理。
ThreadLocalMap 的内部构造
ThreadLocal
的核心在于每个线程内部维护的一个名为 ThreadLocalMap
的映射表。这个映射表存储了线程本地变量的键值对,其中键是 ThreadLocal
对象本身,值则是线程本地变量的具体值。
- ThreadLocalMap 的结构
ThreadLocalMap
是ThreadLocal
的一个内部类,它不是一个可以直接公开访问的数据结构。它的设计目的是为了高效地存储和检索线程本地变量。- 每个
ThreadLocalMap
实例都包含一个数组Entry[]
,该数组的元素是Entry
类型,Entry
是一个静态内部类,它存储了键值对(ThreadLocal
对象和对应的值)。
- get 和 set 方法的实现
- get 方法 :当调用
ThreadLocal
的get
方法时,首先获取当前线程,然后通过线程获取其内部的threadLocals
(即ThreadLocalMap
实例)。如果ThreadLocalMap
存在,则在其中查找当前ThreadLocal
对应的值。查找过程是通过ThreadLocal
对象的哈希值来确定其在Entry
数组中的位置,进而找到对应的值。如果找不到对应的值,则调用initialValue
方法进行初始化。 - set 方法 :当调用
ThreadLocal
的set
方法时,同样先获取当前线程的ThreadLocalMap
。如果ThreadLocalMap
不存在,则创建一个新的ThreadLocalMap
。然后在ThreadLocalMap
中查找当前ThreadLocal
对应的Entry
,如果存在,则更新其值;如果不存在,则创建一个新的Entry
并将其添加到ThreadLocalMap
中。
- get 方法 :当调用
下面是一个简化的 get
和 set
方法的代码示例:
public T get() {Thread currentThread = Thread.currentThread();ThreadLocalMap threadLocalMap = currentThread.threadLocals;if (threadLocalMap != null) {ThreadLocalMap.Entry entry = threadLocalMap.getEntry(this);if (entry != null) {return (T) entry.value;}}return setInitialValue();
}private T setInitialValue() {T value = initialValue();Thread currentThread = Thread.currentThread();ThreadLocalMap threadLocalMap = currentThread.threadLocals;if (threadLocalMap != null) {threadLocalMap.set(this, value);} else {currentThread.threadLocals = new ThreadLocalMap(this, value);}return value;
}public void set(T value) {ThreadLocalMap threadLocalMap = Thread.currentThread().threadLocals;if (threadLocalMap != null) {threadLocalMap.set(this, value);} else {createThreadLocalMap(value);}
}private void createThreadLocalMap(T value) {Thread currentThread = Thread.currentThread();currentThread.threadLocals = new ThreadLocalMap(this, value);
}
ThreadLocal 在子线程中的局限性
虽然 ThreadLocal
在线程隔离方面表现得非常出色,但它也有一个明显的局限性:子线程无法直接获取父线程中的 ThreadLocal
变量值。
案例演示 :
public class ThreadLocalInheritanceIssue {private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) {threadLocal.set("Main Thread Value");new Thread(() -> {System.out.println("Child Thread Value: " + threadLocal.get());}).start();}
}
运行结果 :
Child Thread Value: null
结果分析 :
- 在主线程中,我们设置了
ThreadLocal
变量的值为 “Main Thread Value”。 - 然后启动了一个子线程,在子线程中尝试获取
ThreadLocal
变量的值,结果却是null
。这表明子线程无法直接访问父线程中的ThreadLocal
变量值。
为了解决子线程无法获取父线程 ThreadLocal
变量值的问题,Java 提供了 InheritableThreadLocal
类。InheritableThreadLocal
是 ThreadLocal
的一个子类,它允许子线程继承父线程的线程本地变量值。
InheritableThreadLocal 的实现原理
InheritableThreadLocal
是 ThreadLocal
的一个子类,它允许子线程继承父线程的线程本地变量值。这个特性在某些场景下非常有用,比如在父子线程需要共享某些配置信息时。
- 继承机制的工作原理
- 当子线程通过
new Thread()
的方式创建时,InheritableThreadLocal
会将父线程的ThreadLocalMap
中的键值对复制一份给子线程。这样,子线程就可以访问到父线程的线程本地变量值。 - 但是,如果子线程是从线程池中获取的(即线程复用的情况),
InheritableThreadLocal
将无法正常工作,因为线程池中的线程已经被复用多次,不可能每次都重新复制父线程的ThreadLocalMap
。
- 当子线程通过
- 适用场景与局限性
InheritableThreadLocal
适用于需要父子线程共享线程本地变量值的场景,例如在某些需要传递线程上下文信息的多线程任务中。- 然而,它的局限性在于线程池场景。由于线程池中的线程会被复用,
InheritableThreadLocal
无法保证子线程能够正确继承父线程的线程本地变量值。为了解决这个问题,可以考虑使用其他扩展方案,例如阿里巴巴开源的TransmittableThreadLocal
。
InheritableThreadLocal 的实现原理
- 当子线程通过
new Thread()
的方式创建时,InheritableThreadLocal
会将父线程的ThreadLocalMap
中的键值对复制一份给子线程。这样,子线程就可以访问到父线程的线程本地变量值。 - 但是,如果子线程是从线程池中获取的(即线程复用的情况),
InheritableThreadLocal
将无法正常工作,因为线程池中的线程已经被复用多次,不可能每次都重新复制父线程的ThreadLocalMap
。
代码示例 :
public class InheritableThreadLocalExample {private static final InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();public static void main(String[] args) {inheritableThreadLocal.set("Main Thread Value");new Thread(() -> {System.out.println("Child Thread Value: " + inheritableThreadLocal.get());}).start();}
}
运行结果 :
Child Thread Value: Main Thread Value
结果分析 :
-
在主线程中,我们使用
InheritableThreadLocal
设置了线程本地变量的值为 “Main Thread Value”。 -
启动的子线程通过
InheritableThreadLocal
成功地继承了主线程的线程本地变量值,并正确输出了该值。
虽然 InheritableThreadLocal
解决了子线程继承父线程 ThreadLocal
变量值的问题,但它在使用线程池的场景下存在局限性。由于线程池中的线程会被复用,InheritableThreadLocal
无法保证子线程能够正确继承父线程的线程本地变量值。
案例演示 :
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class InheritableThreadLocalIssue {private static final InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();public static void main(String[] args) {inheritableThreadLocal.set("Main Thread Value");ExecutorService executorService = Executors.newFixedThreadPool(1);// 第一次提交任务executorService.execute(() -> {System.out.println("First Task - Child Thread Value: " + inheritableThreadLocal.get());});try {Thread.sleep(1000); // 确保第一个任务执行完成} catch (InterruptedException e) {e.printStackTrace();}// 第二次提交任务executorService.execute(() -> {System.out.println("Second Task - Child Thread Value: " + inheritableThreadLocal.get());});executorService.shutdown();}
}
运行结果 :
First Task - Child Thread Value: Main Thread Value
Second Task - Child Thread Value: null
结果分析 :
- 在主线程中,我们使用
InheritableThreadLocal
设置了线程本地变量的值为 “Main Thread Value”。 - 第一次提交的任务成功获取到了主线程的线程本地变量值。
- 第二次提交的任务却返回了
null
,这是因为线程池中的线程被复用了,第二次提交的任务并没有继承主线程的线程本地变量值。
为了解决这个问题,阿里巴巴开源了 TransmittableThreadLocal
库。TransmittableThreadLocal
通过在子线程中复制父线程的 ThreadLocal
值,并在线程池任务执行前后进行清理,确保了线程本地变量的正确传递和隔离。
(三)TransmittableThreadLocal 的介绍
TransmittableThreadLocal
是阿里巴巴开源的一个扩展库,它可以解决线程池场景下线程本地变量的传递问题。它通过在子线程中复制父线程的 ThreadLocal
值,并在线程池任务执行前后进行清理,确保了线程本地变量的正确传递和隔离。
使用示例
引入依赖
<dependency><groupId>com.alibaba</groupId><artifactId>transmittable-thread-local</artifactId><version>2.14.3</version>
</dependency>
代码示例
import com.alibaba.transmittable-thread-local.TransmittableThreadLocal;public class TTLExample {private static final TransmittableThreadLocal<String> TTL = new TransmittableThreadLocal<>();public static void main(String[] args) {TTL.set("Main Thread Value");ExecutorService executorService = Executors.newSingleThreadExecutor();executorService.execute(() -> {System.out.println("Child Thread Value: " + TTL.get());TTL.remove();});executorService.shutdown();}
}
总结
ThreadLocal
的工作原理主要依赖于每个线程内部维护的 ThreadLocalMap
,它通过哈希表的方式存储线程本地变量的键值对。InheritableThreadLocal
提供了父子线程之间的变量继承机制,但在使用时需要注意其局限性。对于线程池场景下的变量传递问题,可以借助 TransmittableThreadLocal
等扩展库来解决。
通过深入理解这些原理,我们能够更好地在实际开发中应用 ThreadLocal
及其相关扩展,解决多线程环境下的数据隔离和共享问题。接下来,我们将在实际应用案例中进一步验证这些原理。
以上是关于 ThreadLocal
原理剖析的详细介绍,希望可以帮助读者更好地理解其内部工作机制。
相关文章:
ThreadLocal - 原理与应用场景详解
ThreadLocal 的基础概念 在 Java 的多线程世界里,线程之间的数据共享与隔离一直是一个关键话题。如果处理不当,很容易引发线程安全问题,比如数据混乱、脏读等。而 ThreadLocal 这个工具类,就像是为线程量身定制的 “私人储物柜”…...
VS Code 远程连接服务器:Anaconda 环境与 Python/Jupyter 运行全指南。研0大模型学习(第六、第七天)
VS Code 远程连接服务器:Anaconda 环境与 Python/Jupyter 运行全指南 在使用 VS Code 通过 SSH 远程连接到服务器进行开发时,尤其是在进行深度学习等需要特定环境的工作时,正确配置和使用 Anaconda 环境以及理解不同的代码运行方式非常关键。…...
chili3d调试6 添加左侧面板
注释前 一个一个注释看对应哪个窗口 无事发生 子方法不是显示的窗口 注释掉看看 没了 注释这个看看 零件页面没了 这个浏览器居然完全不用关的,刷新就重载了 注释看看 无工具栏版本 sidebar: 往框框里面加入 div({ className: style.input }, user_…...
Python变量全解析:从基础到高级的命名规则与数据类型指南
一、变量基础与内存机制 1.1 变量的三元构成 每个Python变量由三个核心要素构成: 标识(Identity):对象的内存地址,通过id(obj)获取(如id(name)输出0x5a1b2c3d)类型(Type&am…...
组装一台intel n95纯Linux Server服务器
前言 笔者自己的电脑是macmini m4,平时都是使用虚拟机来充当Linux服务器(系统Ubuntu Server),但是毕竟是ARM CPU,而且黄金内存,开不了几个虚拟机(加内存不划算),所以组装…...
计算机网络中的网络层:架构、功能与重要性
一、网络层概述 在计算机网络的分层模型中,网络层(Network Layer)位于 数据链路层 之上,传输层 之下。网络层的主要任务是处理数据包的路由选择、转发以及分段,使得信息能够从源设备传送到目标设备。它还通过 IP协议&…...
Transformer系列(一):NLP中放弃使用循环神经网络架构
NLP中放弃使用循环神经网络架构 一、符号表示与概念基础二、循环神经网络1. 依赖序列索引存在的并行计算问题2. 线性交互距离 三、总结 该系列笔记阐述了自然语言处理(NLP)中不再采用循环架构(recurrent architectures)的原因&…...
(学习总结34)Linux 库制作与原理
Linux 库制作与原理 库的概念静态库操作归档文件命令 ar静态库制作静态库使用 动态库动态库制作动态库使用与运行搜索路径问题解决方案方案2:建立同名软链接方案3:使用环境变量 LD_LIBRARY_PATH方案4:ldconfig 方案 使用外部库目标文件ELF 文…...
【QT】 QT中的列表框-横向列表框-树状列表框-表格列表框
QT中的列表框-横向列表框-树状列表框-表格列表框 1.横向列表框(1)主要方法(2)信号(3) 示例代码1:(4) 现象:(5) 示例代码2:加载目录项在横向列表框显示(6) 现象: 2.树状列表框 QTreeWidget(1)使用思路(2)信号(3)常用的接口函数(4) 示例代码&am…...
使用DeepSeek的AIGC的内容创作者,如何看待陈望道先生所著的《修辞学发凡》?
目录 1.从修辞手法的运用角度 2.从语言风格的塑造角度 3.从提高创作效率角度 4.从文化传承与创新角度 大家好这里是AIWritePaper官方账号,官网👉AIWritePaper~ 《修辞学发凡》是陈望道 1932 年出版的中国第一部系统的修辞学著作,科学地总…...
使用 GitHub Actions 和 Nuitka 实现 Python 应用(customtkinter ui库)的自动化跨平台打包
目录 引言前置准备配置文件详解实现细节CustomTkinter 打包注意事项完整配置示例常见问题 引言 在 Python 应用开发中,将源代码打包成可执行文件是一个常见需求。本文将详细介绍如何使用 GitHub Actions 和 Nuitka 实现自动化的跨平台打包流程,支持 W…...
【Part 2安卓原生360°VR播放器开发实战】第一节|通过传感器实现VR的3DOF效果
《VR 360全景视频开发》专栏 将带你深入探索从全景视频制作到Unity眼镜端应用开发的全流程技术。专栏内容涵盖安卓原生VR播放器开发、Unity VR视频渲染与手势交互、360全景视频制作与优化,以及高分辨率视频性能优化等实战技巧。 📝 希望通过这个专栏&am…...
【1】云原生,kubernetes 与 Docker 的关系
Kubernetes?K8s? Kubernetes经常被写作K8s。其中的数字8替代了K和s中的8个字母——这一点倒是方便了发推,也方便了像我这样懒惰的人。 什么是云原生? 云原生: 它是一种构建和运行应用程序的方法,它包含&am…...
基于Redis实现RAG架构的技术解析与实践指南
一、Redis在RAG架构中的核心作用 1.1 Redis作为向量数据库的独特优势 Redis在RAG架构中扮演着向量数据库的核心角色,其技术特性完美契合RAG需求: 特性技术实现RAG应用价值高性能内存存储基于内存的键值存储架构支持每秒百万级的向量检索请求分布式架构…...
trivy开源安全漏洞扫描器——筑梦之路
开源地址:https://github.com/aquasecurity/trivy.git 可扫描的对象 容器镜像文件系统Git存储库(远程)虚拟机镜像Kubernetes 在容器镜像安全方面使用广泛,其他使用相对较少。 能够发现的问题 正在使用的操作系统包和软件依赖项…...
pnpm确认全局下载安装了还是显示cnpm不是内部或外部命令,也不是可运行的程序
刚开始是正常使用的。突然开始用不了了一直报错 1.在确保自己node和npm都一直正常使用并且全局安装pnpm的情况下 打开cmd查看npm的环境所在位置 npm config get prefix 2.接着打开高级系统设置 查看自己的path配置有没有问题 确认下载了之后pnpm -v还报错说明没有查询到位置 …...
基于 pnpm + Monorepo + Turbo + 无界微前端 + Vite 的企业级前端工程实践
基于 pnpm Monorepo Turbo 无界微前端 Vite 的企业级前端工程实践 一、技术演进:为什么引入 Vite? 在微前端与 Monorepo 架构落地后,构建性能成为新的优化重点: Webpack 构建瓶颈:复杂配置导致开发启动慢&#…...
软考高级系统架构设计师-第15章 知识产权与标准化
【本章学习建议】 根据考试大纲,本章主要考查系统架构设计师单选题,预计考3分左右,较为简单。 15.1 标准化基础知识 1. 标准的分类 分类 内容 国际标准(IS) 国际标准化组织(ISO)、国际电工…...
MySQL 视图
核心目标: 学习如何创建和使用视图,以简化复杂的查询、提供数据访问控制、实现逻辑数据独立性,并通过 WITH CHECK OPTION 保证数据一致性。 什么是视图? 视图(View)是一种虚拟表,其内容由一个 …...
[操作系统] 信号
信号 vs IPC 板书最后提到了 “信号 vs IPC”,暗示了信号也是一种进程间通信 (Inter-Process Communication, IPC) 的机制。虽然信号的主要目的是事件通知,但它也可以携带少量的信息(即信号的类型)。 初探“信号”——操作系统的“…...
网络基础(协议,地址,OSI模型、Socket编程......)
目录 一、计算机网络发展 二、协议 1.认识协议 2.OSI七层模型 3.TCP/IP 五层(或四层)模型 4.协议本质 三、网络传输流程 1.MAC地址 2.协议栈 3.IP地址 IP地址 vs MAC地址 1. 核心区别 2. 具体通信过程类比 3. 关键总结 为什么需要两者? 4.协议栈图解…...
产品经理学习过程
一:扫盲篇(初始产品经理) 阶段1:了解产品经理 了解产品经理是做什么的、产品经理的分类、产品经理在实际工作中都会接触什么样的岗位、以及产品经理在实际工作中具体要做什么事情。 二:准备篇 阶段2:工…...
深入理解Java包装类:自动装箱拆箱与缓存池机制
深入理解Java包装类:自动装箱拆箱与缓存池机制 对象包装器 Java中的数据类型可以分为两类:基本类型和引用类型。作为一门面向对象编程语言, 一切皆对象是Java语言的设计理念之一。但基本类型不是对象,无法直接参与面向对象操作&…...
Linux中的信号量
目录 信号量概念 定义 操作 类型 应用 信号量封装 一、创建信号量 头文件 函数原型 参数说明 返回值 示例 二、设置信号量初始值 头文件 函数原型 参数解释 返回值 示例 三、信号量的P操作 头文件 函数原型 参数解释 返回值 示例 四、信号量的V操作 示…...
深入理解linux操作系统---第15讲 Web 服务器 Nginx
15.1 Nginx 概述 核心特性与历史背景 Nginx由俄罗斯工程师Igor Sysoev于2002年开发,2004年正式发布,旨在解决传统服务器(如Apache)的C10K问题(即单机万级并发连接处理)。其采用事件驱动(Event…...
深度解析算法之前缀和
25.【模版】一维前缀和 题目链接 描述 输入描述 输出描述 输出q行,每行代表一次查询的结果. 示例 输入: 3 2 1 2 4 1 2 2 3 复制 输出: 3 6 这个题的话就是下面的样子,我们第一行输入 3 2的意思即是这个数组是3个元素大小的数组&…...
混合精度训练中的算力浪费分析:FP16/FP8/BF16的隐藏成本
在大模型训练场景中,混合精度训练已成为降低显存占用的标准方案。然而,通过NVIDIA Nsight Compute深度剖析发现,精度转换的隐藏成本可能使理论算力利用率下降40%以上。本文基于真实硬件测试数据,揭示不同精度格式的计算陷阱。…...
6.8 Python定时任务实战:APScheduler+Cron实现每日/每周自动化调度
Python定时任务实战:APScheduler+Cron实现每日/每周自动化调度 实现每日和每周定时任务 关键词:定时任务调度、Python 原生调度器、Cron 脚本、异常重试机制、任务队列管理 1. 定时任务架构设计 采用 分层调度架构 实现灵活的任务管理: #mermaid-svg-PnZcDOgOklVieQ8X {f…...
[Android] 豆包爱学v4.5.0小学到研究生 题目Ai解析
[Android] 豆包爱学 链接:https://pan.xunlei.com/s/VOODT6IclGPsC7leCzDFz521A1?pwdjxd8# 拍照解析答案 【应用名称】豆包爱学 【应用版本】4.5.0 【软件大小】95mb 【适用平台】安卓 【应用简介】豆包爱学,一般又称河马爱学教育平台app,河马爱学。 关…...
swift-12-Error处理、关联类型、assert、泛型_
一、错误类型 开发过程常见的错误 语法错误(编译报错) 逻辑错误 运行时错误(可能会导致闪退,一般也叫做异常) 2.1 通过结构体 第一步 struct MyError : Errort { var msg: String } 第二步 func divide(_ …...
每日定投40刀BTC(14)20250409 - 20250419
定投 坚持 《磨剑篇》浮生多坎壈,志业久盘桓。松柏凌霜易,骅骝涉险难。砺锋临刃缺,淬火取金残。但使精魂在,重开万象端。...
【刷题Day20】TCP和UDP(浅)
TCP 和 UDP 有什么区别? TCP提供了可靠、面向连接的传输,适用于需要数据完整性和顺序的场景。 UDP提供了更轻量、面向报文的传输,适用于实时性要求高的场景。 特性TCPUDP连接方式面向连接无连接可靠性提供可靠性,保证数据按顺序…...
大数据建模与评估
文章目录 实战案例:电商用户分群与价值预测核心工具与库总结一、常见数据挖掘模型原理及应用(一)决策树模型(二)随机森林模型(三)支持向量机(SVM)模型(四)K - Means聚类模型(五)K - Nearest Neighbors(KNN)模型二、运用Python机器学习知识实现数据建模与评估(一…...
Python语法系列博客 · 第6期[特殊字符] 文件读写与文本处理基础
上一期小练习解答(第5期回顾) ✅ 练习1:字符串反转模块 string_tools.py # string_tools.py def reverse_string(s):return s[::-1]调用: import string_tools print(string_tools.reverse_string("Hello")) # 输出…...
Pandas取代Excel?
有人在知乎上提问:为什么大公司不用pandas取代excel? 而且列出了几个理由:Pandas功能比Excel强大,运行速度更快,Excel除了简单和可视化界面外,没有其他更多的优势。 有个可怕的现实是,对比Exce…...
《解锁图像“高清密码”:超分辨率重建之路》
在图像的世界里,高分辨率意味着更多细节、更清晰的画面,就像用高清望远镜眺望远方,一切都纤毫毕现。可现实中,我们常被低分辨率图像困扰,模糊的监控画面、老旧照片里难以辨认的面容……不过别担心,图像超分…...
杨校老师课堂之C++入门练习题梳理
采用C完成下列题目,要求每题目的时间限制:1秒 内存限制:128M 1. 交换个位与十位的数字 时间限制:1秒 内存限制:128M 题目描述 试编写一个程序,输入一个两位数,交换十位与个位上的数字并输出。 …...
基于springboot的老年医疗保健系统
博主介绍:java高级开发,从事互联网行业六年,熟悉各种主流语言,精通java、python、php、爬虫、web开发,已经做了六年的毕业设计程序开发,开发过上千套毕业设计程序,没有什么华丽的语言࿰…...
数据分析与挖掘
一 Python 基本语法 变量与数据类型 : Python 中变量无需声明,直接赋值即可。 常见的数据类型有数值型(整型 int、浮点型 float、复数型 complex)、字符串型(str,用单引号、双引号或三引号括起来ÿ…...
RoBoflow数据集的介绍
https://public.roboflow.com/object-detection(该数据集的网址) 可以看到一些基本情况 如果我们想要下载,直接点击 点击图像可以看到一些基本情况 可以点击红色箭头所指,右边是可供选择的一些yolo模型的格式 如果你想下载…...
大模型Rag - 两大检索技术
一、稀疏检索:关键词匹配的经典代表 稀疏检索是一种基于关键词统计的传统检索方法。其基本思想是:通过词频和文档频率来衡量一个文档与查询的相关性。 核心原理 文档和查询都被表示为稀疏向量(如词袋模型),只有在词…...
【T型三电平仿真】SVPWM调制
目录 仿真模型分析 克拉克变换 大扇区判断编辑 小区域判断 计算基本电压矢量作用时间 确定基本电压矢量的作用顺序 作用时间和矢量作用顺序对应 七段式化生成阶梯图 矢量状态分布 本人学习过程中提出的问题和解释 SVPWM调制实现了什么功能 SVPWM的算法步骤是什么…...
树莓派5-开发应用笔记
0.树莓派系统目录 /home:用户目录。 除了root用户外,其他所有的使用者的数据都存放在这个目录下,在树莓派的系统中,/home目录中有一个pi的子目录,这个就是pi用户的默认目录。 /bin: 主要放置系统的必备执行文件目录。 …...
[Java实战经验]异常处理最佳实践
一些好的异常处理实践。 目录 异常设计自定义异常为异常设计错误代码(状态码)设计粒度全局异常处理异常日志信息保留 异常处理时机资源管理try-with-resources异常中的事务 异常设计 自定义异常 自定义异常设计,如业务异常定义BusinessExce…...
AOSP的Doze模式-LightIdle初识
前言 从Android 6.0开始,谷歌引入了Doze模式(打盹模式)的省电技术延长电池使用时间。根据第三方测试显示,两台同样的Nexus 5,开启的Doze的一台待机能达到533小时,而未开启Doze的一台待机只能达到200小时。Doze省电效果十分明显。…...
QML动画--ParticleSystem
ParticleSystem 是 QML 中用于创建和管理粒子系统的组件,可以制作各种粒子效果如火焰、烟雾、爆炸等。 基本用法 qml import QtQuick.Particles 2.15ParticleSystem {id: particleSystemImageParticle {source: "particle.png"color: "red"a…...
Win 11 重装 Ubuntu 双系统方法
有时候 Ubuntu 环境崩溃了,或者版本过低,需要卸载重装。本文介绍重装的方法,默认已经有一个双系统。 1. 删除原先 Ubuntu 分区 首先打开 Win 的磁盘管理,找到 Ubuntu 的分区,右键删除分区(注意不要错删 wi…...
单例模式:懒汉式的两种优化写法
单例模式:全局唯一实例 懒汉式:获取时才初始化 ①静态局部变量实现(Meyer’s Singleton)【推荐】 /* 类内创建自身实例的可行性分析:在C中,类可以通过静态成员函数创建自身实例。这种机制的核心在于&…...
详细解释浏览器是如何渲染页面的?
渲染流程概述 渲染的目标:将HTML文本转化为可以看到的像素点 当浏览器的网络线程收到 HTML 文档后,会产生一个渲染任务,并将其传递给渲染主线程的消息队列。在事件循环机制的作用下,渲染主线程取出消息队列中的渲染任务࿰…...
高速系统设计简介
1.1 PCB 设计技术回顾 1981 年 8 月 12 日,IBM 正式发布了历史上第一台个人电脑,自此之后,个人电脑融入了人们生活和工作的各个角落,人类从此进入了个人电脑时代。个人电脑的出现,不仅促进了电子产品在消费领域的发展…...