ThreadLocal - 原理与应用场景详解

ThreadLocal 的基础概念

在 Java 的多线程世界里,线程之间的数据共享与隔离一直是一个关键话题。如果处理不当,很容易引发线程安全问题,比如数据混乱、脏读等。而 ThreadLocal 这个工具类,就像是为线程量身定制的 “私人储物柜”,为每个线程提供了独立的存储空间,完美地解决了线程间数据隔离的问题。

ThreadLocal 是什么?

ThreadLocal 是 Java 中一个非常实用的类,它为每个线程都提供了自己独立的变量副本。换句话说,每个线程都可以通过 ThreadLocal 来设置(set)和获取(get)自己的私有变量,而不会和其他线程产生任何干扰,就像每个线程都有自己的 “小金库”,互不干扰,互不影响。

举个简单的例子,假如我们有一个变量 count,在普通情况下,多个线程同时访问这个变量时,很容易出现数据混乱的情况,因为它们都操作的是同一个内存地址的变量。但如果我们把 count 放到 ThreadLocal 中,那么每个线程都会有自己独立的 count 副本,线程 A 对它的 count 副本进行修改,完全不会影响到线程 B 的 count 副本。

ThreadLocal 的基本功能与特点

  • 线程隔离 :这是 ThreadLocal 最显著的特点。每个线程对 ThreadLocal 变量的读写操作都局限在自己的线程内,完全不会与其他线程产生数据共享或冲突。这种线程隔离的特性使得 ThreadLocal 在处理一些需要线程私有数据的场景时非常有用,比如在每个线程中保存独立的配置信息、用户身份信息等。
  • 无需显式加锁 :由于线程间的数据隔离,使用 ThreadLocal 变量时,不需要像操作共享变量那样使用显式的锁机制(如 synchronizedReentrantLock)来保证线程安全。这大大简化了多线程编程的复杂度,提高了开发效率。

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 的结构
    • ThreadLocalMapThreadLocal 的一个内部类,它不是一个可以直接公开访问的数据结构。它的设计目的是为了高效地存储和检索线程本地变量。
    • 每个 ThreadLocalMap 实例都包含一个数组 Entry[],该数组的元素是 Entry 类型,Entry 是一个静态内部类,它存储了键值对(ThreadLocal 对象和对应的值)。
  • get 和 set 方法的实现
    • get 方法 :当调用 ThreadLocalget 方法时,首先获取当前线程,然后通过线程获取其内部的 threadLocals(即 ThreadLocalMap 实例)。如果 ThreadLocalMap 存在,则在其中查找当前 ThreadLocal 对应的值。查找过程是通过 ThreadLocal 对象的哈希值来确定其在 Entry 数组中的位置,进而找到对应的值。如果找不到对应的值,则调用 initialValue 方法进行初始化。
    • set 方法 :当调用 ThreadLocalset 方法时,同样先获取当前线程的 ThreadLocalMap。如果 ThreadLocalMap 不存在,则创建一个新的 ThreadLocalMap。然后在 ThreadLocalMap 中查找当前 ThreadLocal 对应的 Entry,如果存在,则更新其值;如果不存在,则创建一个新的 Entry 并将其添加到 ThreadLocalMap 中。

下面是一个简化的 getset 方法的代码示例:

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 类。InheritableThreadLocalThreadLocal 的一个子类,它允许子线程继承父线程的线程本地变量值。

InheritableThreadLocal 的实现原理

InheritableThreadLocalThreadLocal 的一个子类,它允许子线程继承父线程的线程本地变量值。这个特性在某些场景下非常有用,比如在父子线程需要共享某些配置信息时。

  • 继承机制的工作原理
    • 当子线程通过 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 原理剖析的详细介绍,希望可以帮助读者更好地理解其内部工作机制。

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

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

相关文章

iwebsec靶场 文件包含关卡通关笔记11-ssh日志文件包含

目录 日志包含 1.构造恶意ssh登录命令 2.配置ssh日志开启 &#xff08;1&#xff09;配置sshd &#xff08;2&#xff09;配置rsyslog &#xff08;3&#xff09;重启服务 3.写入webshell木马 4.获取php信息渗透 5.蚁剑连接 日志包含 1.构造恶意ssh登录命令 ssh服务…

Diamond软件的使用--(4)搭建Modelsim仿真库

使用Modelsim仿真的原因 由于diamond自带的仿真软件Active-HDL需要另一套Lisence&#xff0c;所以我们使用第三方仿真软件Modelsim来进行仿真。 Modelsim10.5下载链接如下&#xff1a;https://pan.baidu.com/s/1G9699ocWm1UTqK2yS2igyQ 提取码&#xff1a;lewa 一、Lattice仿…

2025年4月19日,四月第三周,C++,字符串数组答案解析

答案与解析 1. 输出字符串数组所有元素 cpp 复制 下载 #include <iostream> using namespace std;int main() {string arr[] = {"apple", "banana", "cherry"};int n = sizeof(arr)/sizeof(arr[0]); // 计算数组长度for (int i = 0; …

C语言之高校学生信息快速查询系统的实现

&#x1f31f; 嗨&#xff0c;我是LucianaiB&#xff01; &#x1f30d; 总有人间一两风&#xff0c;填我十万八千梦。 &#x1f680; 路漫漫其修远兮&#xff0c;吾将上下而求索。 C语言之高校学生信息快速查询系统的实现 目录 任务陈述与分析 问题陈述问题分析 数据结构设…

【网络篇】TCP vs UDP底层区别+网络编程概念

大家好呀 我是浪前 今天讲解的是网络篇的第三章&#xff1a;网络编程概念和TCP&UDP的区别 网络编程概念TCP和UDP的区别 跨主机通信:网络编程插座&#xff1a;网络编程的本质&#xff1a; 网络编程的重要概念&#xff1a;客户端和服务器&#xff1a; 客户端和服务器的交互模…

EMIF详解

一、EMIF的基本定义 EMIF&#xff08;External Memory Interface&#xff0c;外部存储器接口&#xff09; 是嵌入式处理器&#xff08;如DSP、FPGA、SoC&#xff09;用于连接外部存储器的专用硬件接口模块&#xff0c;负责管理处理器与存储器之间的地址/数据总线、控制信号及时…

Keil MDK 编译问题:function “HAL_IncTick“ declared implicitly

问题与处理策略 问题描述 ..\..\User\stm32f1xx_it.c(141): warning: #223-D: function "HAL_IncTick" declared implicitlyHAL_IncTick(); ..\..\User\stm32f1xx_it.c: 1 warning, 0 errors问题原因 在 stm32f1xx_it.c 文件中调用了 HAL_IncTick()&#xff0c;但…

Java Web项目(一)

框架 java web项目总工分为两部分&#xff1a;客户端&#xff08;前端&#xff09;和服务端&#xff08;后端&#xff09; 客户端发起请求&#xff0c;服务端接受请求并进行处理 发起请求的方式&#xff1a;from表单、jQuery ajax from表单 造成全局的变化&#xff0c;在发…

Dify部署内网时遇到的代理问题及解决办法

大家知道&#xff0c;在公网环境下利用docker安装dify源码镜像比较容易&#xff0c;详见我之前的文章&#xff0c;基于dify开发agent、workflow等非常方便&#xff0c;本次想着在内部网络环境下也完成部署&#xff0c;以方便更多的人使用&#xff0c;但在部署到内网环境下&…

多节点监控的docker管理面板Portainer安装指南:家庭云计算专家

背景 Portainer 是一个轻量级且功能强大的容器管理面板&#xff0c;专为 Docker 和 Kubernetes 环境设计。它通过直观的 Web 界面简化了容器的部署、管理和监控&#xff0c;即使是非技术用户也能轻松上手。Portainer 支持多节点管理&#xff0c;允许用户从一个中央控制台管理多…

Linux内核哈希表学习笔记

前沿 近期项目中需要给自定义的驱动增加一个功能存储相关的数据信息。结合实际业务层面,最终决定采用哈希表的结构来存储。因为其具备快速查找,插入和删除。其实现原理通过散列函数映射到指定位置。时间复杂度O(1).而且运算速度也快,很适合处理大量的数据场景。但是其也有一…

对于在线教育或知识付费类网站视频处理方案

一、视频格式&#xff1a; 1. 推荐格式&#xff1a;HLS&#xff08;HTTP Live Streaming&#xff09; 优势‌&#xff1a; ‌自适应码率‌&#xff1a;根据用户网络状况自动切换清晰度&#xff0c;避免卡顿。‌广泛兼容性‌&#xff1a;iOS/macOS 原生支持&#xff0c;Android…

Deepseek输出的内容如何直接转化为word文件?

我们有时候会直接利用deepseek翻译别人的文章或者想将deepseek输出的内容直接复制到word文档里。但是文本格式和word是不对应的。这时候需要输入如下命令&#xff1a; 以上翻译内容的格式和排版要求如下&#xff1a; 1、一级标题 字体为黑体&#xff08;三号&#xff09;&…

【Vue】组件通信(Props/Emit、EventBus、Provide/Inject)

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Vue 文章目录 1. Props/Emit 父子组件通信1.1 Props 向下传递数据1.2 Emit 向上传递事件 2. EventBus 跨组件通信2.1 创建事件总线2.2 使用事件总线2.3 EventBus 优缺点 3. Provide/Inject 深层组件通信3.1 基本使用3.2 响应式处…

vulnhub sunset系列靶机合集(部分)

描述&#xff1a;该合集包含sunset系列适合新手的四个靶机&#xff08;sunset:1、dusk、sunrise、noontide&#xff09;的渗透全过程。 靶机下载地址&#xff1a;Vulnerable By Design - Search: sunset ~ VulnHubhttps://www.vulnhub.com/?qsunset sunset:1 渗透过程 信息…

【MySQL】MySQL的基础语法及其语句的介绍

1、基础语法 mysql -h【主机名】 -u【用户名】 -p //登录MySQL exit或quit; //退出MySQL show database; //查看MySQL下的所有数据库 use 【数据库名】; //进入数据库 show tables; //查看数据库下的所有表名 *MySQL的启动和关闭 &am…

2025-4-20-C++ 学习 数组(1)

数组 2025-4-20-C++ 学习 数组(1)P1428 小鱼比可爱题目描述输入格式输出格式输入输出样例 #1输入 #1输出 #1说明/提示题解代码P1427 小鱼的数字游戏题目描述输入格式输出格式输入输出样例 #1输入 #1输出 #1说明/提示数据规模与约定题解代码P5727 【深基5.例3】冰雹猜想题目描…

ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(显示输出类外设之LCD)

目录 ESP-ADF外设子系统深度解析&#xff1a;esp_peripherals组件架构与核心设计&#xff08;显示输出类外设之LCD&#xff09;简介模块概述功能定义架构位置核心特性 LCD外设分析LCD外设概述LCD外设层次架构图 LCD外设API和数据结构外设层API公共API内部数据结构 LCD外设配置选…

面试题:循环引用两个节点相互引用,如何判断哪个用 shared_ptr?哪个用 weak_ptr?

目录 1.引言 2.原理 3.所有权模型与指针选择 4.复杂场景的决策策略 5.注意事项 6.总结 1.引言 当两个对象通过 shared_ptr 相互引用时&#xff0c;会产生循环引用问题&#xff0c;导致内存泄漏。因为这两个对象的引用计数永远不会变为 0&#xff0c;即使它们在程序的其他…

QT聊天项目DAY06

1.从git上同步项目 编译测试&#xff0c;编译通过 Post请求测试 测试成功 2. email is 打印有问题&#xff0c;检查 解析结果是存储在jsonResult中的&#xff0c;修改 3. 客户端实现Post验证码请求 3.1 同步Qt客户端项目 检查QT版本&#xff0c;由于我在公司用的还是QT5.12.9…