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

第30周Java分布式入门 ThreadLocal

ThreadLocal 课程笔记

一、章节结构概述

本章主要学习重要的工具类 ThreadLocal。章节分为六大模块:

  1. ThreadLocal 的两大使用场景
  2. ThreadLocal 所带来的好处
  3. ThreadLocal 的主要方法及使用顺序
  4. ThreadLocal 原理源码分析
  5. 使用 ThreadLocal 的注意点和使用规范

从下一个小节开始,将逐一展开这六大模块的内容。

二、两大使用场景

场景一:每个线程需要一个独享的对象

  • 背景:某些工具类本身不是线程安全的,如果多个线程共享同一个静态工具类,会有很大风险,甚至肯定会出错。
  • 解决方案:使用 ThreadLocal 为每个线程制作一个独享的对象。不同线程拥有的是不同实例,相互之间不会影响。
  • 典型例子:SimpleDateFormat 和 Random 这两个工具类都是线程不安全的,使用 ThreadLocal 非常合适。

场景二:每个线程内需要保存一些全局信息

  • 背景:在拦截器中获取到的用户信息,希望在同一个线程的不同方法中直接调用,避免每次传递参数的繁琐。
  • 解决方案:利用 ThreadLocal 在线程内保存全局信息。例如,请求进来后在拦截器中将 token 转成用户信息并保存,后续该线程调用的其他方法(如买商品、更新库存、日志记录、抽奖等)可以直接获取用户信息,无需层层传递参数。
  • 优势:简化代码,提高可读性和维护性。

这两种场景有明显区别:第一种场景侧重于解决工具类线程不安全问题,让每个线程有独立工具类实例;第二种场景主要解决参数传递麻烦的问题。

ThreadLocal 课程笔记

一、第一大使用场景:每个线程需要一个独享的对象

场景描述

每个线程需要一个独享的对象,这些对象通常是工具类实例。由于工具类本身不是线程安全的,多个线程共享同一个实例会有风险,因此需要为每个线程提供独立的实例。

比喻

一个班级有30个同学,只有一本教材。如果大家都抢着看,就会发生线程安全问题。使用ThreadLocal相当于复印30份教材,每人一份,互不影响。

ThreadLocal 的命名由来

ThreadLocal中的"Thread"代表线程,"Local"代表本地。每个线程只能访问自己的实例副本,其他线程无法访问,不存在多线程共享问题。

代码示例:SimpleDateFormat 的进化之路

第一步:两个线程,各自创建对象
package thread_local;
public class ThreadLocalNormalUsage00 {public static String date(long second) {// 将秒转换为毫秒Date date = new Date(second * 1000);// 定义日期格式SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 格式化日期return dateFormat.format(date);}public static void main(String[] args) {// 线程1:打印10秒后的时间new Thread(() -> System.out.println(date(10))).start();// 线程2:打印1007秒后的时间new Thread(() -> System.out.println(date(1007))).start();}
}
第二步:扩展到多个线程
package thread_local;
public class ThreadLocalNormalUsage01 {public static String date(long second) {Date date = new Date(second * 1000);SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return dateFormat.format(date);}public static void main(String[] args) {// 使用线程池ExecutorService threadPool = Executors.newFixedThreadPool(10);for (int i = 0; i < 1000; i++) {final long taskSecond = i;threadPool.submit(() -> {System.out.println(date(taskSecond));});}threadPool.shutdown();}
}
第三步:使用静态对象引发线程安全问题
package thread_local;
public class ThreadLocalNormalUsage02 {private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static String date(long second) {Date date = new Date(second * 1000);return DATE_FORMAT.format(date);}public static void main(String[] args) {ExecutorService threadPool = Executors.newFixedThreadPool(10);for (int i = 0; i < 1000; i++) {final long taskSecond = i;threadPool.submit(() -> {System.out.println(date(taskSecond));});}threadPool.shutdown();}
}
第四步:使用 synchronized 解决线程安全问题
package thread_local;
public class ThreadLocalNormalUsage03 {private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static String date(long second) {Date date = new Date(second * 1000);synchronized (ThreadLocalNormalUsage03.class) {return DATE_FORMAT.format(date);}}public static void main(String[] args) {ExecutorService threadPool = Executors.newFixedThreadPool(10);for (int i = 0; i < 1000; i++) {final long taskSecond = i;threadPool.submit(() -> {System.out.println(date(taskSecond));});}threadPool.shutdown();}
}
第五步:使用 ThreadLocal 解决问题
package thread_local;
public class ThreadLocalNormalUsage04 {private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT_THREAD_LOCAL = new ThreadLocal<SimpleDateFormat>() {@Overrideprotected SimpleDateFormat initialValue() {return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");}};public static String date(long second) {Date date = new Date(second * 1000);SimpleDateFormat dateFormat = DATE_FORMAT_THREAD_LOCAL.get();return dateFormat.format(date);}public static void main(String[] args) {ExecutorService threadPool = Executors.newFixedThreadPool(10);for (int i = 0; i < 1000; i++) {final long taskSecond = i;threadPool.submit(() -> {System.out.println(date(taskSecond));});}threadPool.shutdown();}
}
使用 Lambda 表达式简化 ThreadLocal 初始化
package thread_local;
public class ThreadLocalNormalUsage05 {private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT_THREAD_LOCAL = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));public static String date(long second) {Date date = new Date(second * 1000);SimpleDateFormat dateFormat = DATE_FORMAT_THREAD_LOCAL.get();return dateFormat.format(date);}public static void main(String[] args) {ExecutorService threadPool = Executors.newFixedThreadPool(10);for (int i = 0; i < 1000; i++) {final long taskSecond = i;threadPool.submit(() -> {System.out.println(date(taskSecond));});}threadPool.shutdown();}
}

总结

通过以上代码示例,我们逐步展示了如何使用ThreadLocal解决多线程环境下工具类实例的线程安全问题。最终方案不仅保证了线程安全,还避免了synchronized带来的性能损耗,实现了高效的内存利用。每个线程都有自己的独享对象,不同线程之间互不干扰,完美契合了ThreadLocal的设计初衷。

ThreadLocal 详细笔记

一、ThreadLocal 第二个使用场景:避免参数传递的麻烦

(一)场景描述

在实际开发中,一个请求可能需要调用多个方法,每个方法都需要使用到某些相同的对象(如用户信息)。如果每次都通过参数传递这些对象,会导致代码冗余且难以维护。

(二)传统解决方案的弊端

  • 直接使用 static 变量:不可行,因为多个请求对应的用户信息不同,static 变量会导致数据混乱。
  • 使用 Map:虽然可以存储每个线程的用户信息,但需要保证线程安全,使用 synchronized 或 ConcurrentHashMap 会影响性能。

(三)ThreadLocal 的优势

ThreadLocal 无需同步,也不需要 ConcurrentHashMap,可以在不影响性能的情况下,避免层层传递参数,直接达到共享对象的目的。

(四)代码示例:使用 ThreadLocal 避免参数传递

1. 创建 User 类
public class User {private String name;public User(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
2. 创建 ServiceOne 类
public class ServiceOne {public void process(User user) {UserContextHolder.setUser(user);}
}
3. 创建 UserContextHolder 类
public class UserContextHolder {private static final ThreadLocal<User> USER_THREAD_LOCAL = new ThreadLocal<>();public static void setUser(User user) {USER_THREAD_LOCAL.set(user);}public static User getUser() {return USER_THREAD_LOCAL.get();}
}
4. 创建 ServiceTwo 和 ServiceThree 类
public class ServiceTwo {public void process() {User user = UserContextHolder.getUser();System.out.println("ServiceTwo: " + user.getName());}
}public class ServiceThree {public void process() {User user = UserContextHolder.getUser();System.out.println("ServiceThree: " + user.getName());}
}
5. 创建主函数进行测试
public class ThreadLocalExample {public static void main(String[] args) {ServiceOne serviceOne = new ServiceOne();ServiceTwo serviceTwo = new ServiceTwo();ServiceThree serviceThree = new ServiceThree();User user = new User("超哥");serviceOne.process(user);serviceTwo.process();serviceThree.process();}
}
6. 运行结果
ServiceTwo: 超哥
ServiceThree: 超哥

(五)总结

在这个示例中,我们通过 ThreadLocal 在不同的方法之间共享了 User 对象,而无需通过参数传递。每个线程都有自己的 User 对象副本,避免了线程安全问题,同时提高了代码的可读性和维护性。这种方法特别适用于需要在多个方法中共享某些对象的场景,如用户信息、事务 ID 等。

二、知识点总结

  1. ThreadLocal 的第二个使用场景:避免参数传递的麻烦。
  2. 传统解决方案的弊端
    • 使用 static 变量会导致数据混乱。
    • 使用 Map 需要保证线程安全,影响性能。
  3. ThreadLocal 的优势:无需同步,不影响性能,避免层层传递参数。
  4. User 类:定义用户信息的类,包含姓名等属性。
  5. ServiceOne 类:模拟业务场景,将用户信息设置到 ThreadLocal 中。
  6. UserContextHolder 类:持有 ThreadLocal 实例,提供设置和获取用户信息的方法。
  7. ServiceTwo 和 ServiceThree 类:模拟业务场景,从 ThreadLocal 中获取用户信息并使用。
  8. 主函数测试:创建服务实例,模拟用户请求,验证 ThreadLocal 的使用效果。
  9. 运行结果:验证了通过 ThreadLocal 成功在不同方法间共享用户信息。

ThreadLocal 课程笔记

一、ThreadLocal 的重要方法解析

(一)initialValue 方法

  1. 作用 :返回当前线程对应的初始值,且该方法是延迟加载的,只有在调用 get 方法时才会触发。
  2. 源码示例 :在 ThreadLocal 的 get 方法中,若检测到值未设置,则会调用 initialValue 方法来初始化值。
  3. 特点
    • 若未重写 initialValue 方法,默认返回 null。在需要初始值的场景下,必须重写该方法。
    • 第一次调用 get 方法时会间接执行 initialValue 方法,但如果在此之前已使用 set 方法设置值,则不再执行 initialValue 方法。
    • 每个线程最多只需调用一次 initialValue 方法,后续调用 get 方法不会再次触发,除非先调用 remove 方法删除后再 get。

(二)set 方法

  1. 作用 :为当前线程设置一个值,设置后其他方法可通过 get 方法获取到该值。
  2. 使用场景 :当需要手动为线程设置特定值时使用,与 initialValue 方法的自动初始化形成对比。

(三)get 方法

  1. 作用 :获取当前线程所对应的值,若第一次调用且未设置值,则会触发 initialValue 方法进行初始化。
  2. 使用场景 :在需要获取线程本地存储的值时调用,贯穿 ThreadLocal 的各种使用场景。

(四)remove 方法

  1. 作用 :删除当前线程所保存的值。
  2. 使用场景及影响
    • 当业务流程需要清空线程本地存储的值时使用,如在某个服务处理完成后不希望后续服务再获取到之前的值。
    • 调用 remove 后,若后续再次调用 get 方法,在未重新 set 值的情况下,会重新触发 initialValue 方法(若已重写)或返回 null。
    • 示例:在场景二的代码演示中,若 service 二调用 remove 方法清空用户信息,service 三将获取到 null,导致空指针异常;但可在 remove 后重新 set 新值供后续服务使用。

二、知识点总结

  1. initialValue 方法 :用于设置线程初始值,延迟加载,调用 get 方法时触发,未重写默认返回 null,每个线程最多调用一次,除非 remove 后重新 get。
  2. set 方法 :手动为线程设置值,设置后后续 get 可获取该值。
  3. get 方法 :获取线程对应的值,第一次调用若未设置则初始化。
  4. remove 方法 :删除线程保存的值,清空后可重新设置新值。
  5. 方法使用场景 :initialValue 适用于需要自动初始化值的情况;set 和 get 配合用于手动控制值的设置与获取;remove 用于清理线程本地存储,避免值在不同业务环节间的不当传递。

ThreadLocal 课程笔记

一、ThreadLocal 源码分析

(一)ThreadLocal 的结构及三个重要组件的关系

  1. 三个重要组件 :Thread、ThreadLocal 和 ThreadLocalMap。

    • Thread :线程类,每个线程持有一个 ThreadLocalMap 类型的成员变量。
    • ThreadLocal :为每个线程提供独立的变量副本,通过 ThreadLocalMap 实现线程与值的映射。
    • ThreadLocalMap :存储在 Thread 类中,用于保存多个 ThreadLocal 和其对应的值,以键值对形式存储。
  2. 关系 :每个线程有一个 ThreadLocalMap,ThreadLocal 作为键,对应的值存储在该线程的 ThreadLocalMap 中。

(二)重要方法的源码解析

  1. get 方法

    • 首先获取当前线程的 ThreadLocalMap。
    • 如果 ThreadLocalMap 为空,调用 initialValue 方法初始化值。
    • 如果 ThreadLocalMap 不为空,以当前 ThreadLocal 为键调用 ThreadLocalMap 的 getEntry 方法获取值。
  2. set 方法

    • 获取当前线程的 ThreadLocalMap。
    • 如果 ThreadLocalMap 为空,创建新的 ThreadLocalMap 并设置值。
    • 如果 ThreadLocalMap 不为空,以当前 ThreadLocal 为键,将新值存入 ThreadLocalMap。
  3. remove 方法 :获取当前线程的 ThreadLocalMap,并以当前 ThreadLocal 为键,从 ThreadLocalMap 中删除对应的键值对。

  4. initialValue 方法 :默认返回 null,可重写该方法为 ThreadLocal 设置初始值,仅在第一次调用 get 方法且未设置值时触发。

(三)ThreadLocalMap 的实现细节

  1. 结构 :ThreadLocalMap 是一个 Entry 数组,每个 Entry 包含一个 ThreadLocal 类型的键和一个 Object 类型的值。
  2. 哈希冲突处理 :采用线性探测法,与 HashMap 的拉链法不同,发生冲突时继续查找下一个空位置存储。

二、ThreadLocal 使用的注意点

(一)内存泄露问题

  1. 原因 :ThreadLocalMap 中的键为 ThreadLocal 的弱引用,值为强引用。如果 ThreadLocal 不再被使用但线程未终止,其对应的值无法被垃圾回收,导致内存泄露。
  2. 解决方法 :在不再使用 ThreadLocal 时,主动调用 remove 方法删除键值对。

(二)空指针异常问题

  1. 原因 :在未设置初始值或未调用 set 方法的情况下直接调用 get 方法,若 ThreadLocal 的泛型为包装类型且涉及装箱拆箱操作,可能会导致空指针异常。
  2. 解决方法 :在使用 ThreadLocal 时,确保正确设置初始值或在使用前调用 set 方法。

三、知识点总结

  1. ThreadLocal 的结构 :由 Thread、ThreadLocal 和 ThreadLocalMap 三个组件构成,每个线程拥有自己的 ThreadLocalMap,用于存储 ThreadLocal 和对应值。
  2. 重要方法的源码解析 :get 方法获取值,set 方法设置值,remove 方法删除值,initialValue 方法设置初始值,ThreadLocalMap 的实现细节包括 Entry 数组结构和线性探测法处理哈希冲突。
  3. 使用注意点 :避免内存泄露需主动调用 remove 方法,防止空指针异常需正确设置初始值或调用 set 方法。

ThreadLocal 课程笔记

一、ThreadLocal 章节回顾

(一)两大使用场景

  1. 场景一:工具类线程安全问题

    • 问题 :如 SimpleDateFormat 等工具类线程不安全,多线程使用时易出错。
    • 解决方案 :使用 ThreadLocal 为每个线程提供独立副本,避免线程安全问题。
    • 示例 :从两个线程扩展到 1000 个线程,使用线程池后,通过 ThreadLocal 解决静态实例带来的线程安全问题,无需同步,提高效率。
  2. 场景二:方法间传递公用信息

    • 问题 :用户信息等在多个方法间传递,层层传参繁琐且易出错。
    • 解决方案 :使用 ThreadLocal 在线程内保存全局信息,各方法可直接获取,无需传参。
    • 示例 :通过 ServiceOne 设置用户信息,ServiceTwo 和 ServiceThree 直接获取,避免传参。

(二)两种场景对应的用法

  1. 场景一 :需实现 initialValue 方法,适用于对象初始化可控场景。
  2. 场景二 :使用 set 方法,适用于对象初始化时间不受控制场景(如拦截器),无需重写 initialValue 方法。

(三)ThreadLocal 的好处

  1. 线程安全 :每个线程拥有独立副本,无需同步。
  2. 高效 :避免加锁带来的性能损耗。
  3. 节省资源 :无需创建大量对象,节省内存和开销。
  4. 便捷 :随时随地获取之前保存的对象,免去传参麻烦。

(四)重要方法解析

  1. initialValue 方法 :设置初始值,延迟加载,仅在第一次调用 get 且未设置值时触发,通常只调用一次,不重写默认返回 null。
  2. set 方法 :设置新值,与 initialValue 二选一。
  3. get 方法 :获取之前设置的值,若未设置则触发 initialValue(若重写)。
  4. remove 方法 :删除对应值,仅删除当前 ThreadLocal 的键值对,不影响其他。

(五)源码分析

  1. 关键组件关系 :Thread(线程)、ThreadLocal 和 ThreadLocalMap 三者关系,每个线程持有一个 ThreadLocalMap,用于存储多个 ThreadLocal 和对应值。
  2. 方法实现原理 :get、set、remove 方法均通过操作 ThreadLocalMap 实现,ThreadLocalMap 使用 Entry 数组存储键值对,处理哈希冲突采用线性探测法。

(六)使用注意点

  1. 内存泄露 :因 ThreadLocalMap 中键为弱引用,值为强引用,线程不终止时可能导致内存泄露,需主动调用 remove 方法。
  2. 空指针异常 :未设置初始值或未调用 set,直接 get 时若涉及装箱拆箱操作,可能抛出空指针异常,应正确设置初始值或调用 set 方法。

二、知识点总结

  1. 两大使用场景 :解决工具类线程安全问题和方法间传递公用信息,分别对应不同用法。
  2. 重要方法 :initialValue、set、get 和 remove,理解其功能和触发条件。
  3. 源码结构 :Thread、ThreadLocal 和 ThreadLocalMap 的关系及方法实现原理。
  4. 使用注意点 :避免内存泄露和空指针异常,正确使用 ThreadLocal。

相关文章:

第30周Java分布式入门 ThreadLocal

ThreadLocal 课程笔记 一、章节结构概述 本章主要学习重要的工具类 ThreadLocal。章节分为六大模块&#xff1a; ThreadLocal 的两大使用场景ThreadLocal 所带来的好处ThreadLocal 的主要方法及使用顺序ThreadLocal 原理源码分析使用 ThreadLocal 的注意点和使用规范 从下一…...

Windows 10 LTSC 2019 中文版下载及安装教程(附安装包)

&#xff08;cn_windows_10_enterprise_ltsc_2019_x64_dvd_9c09ff24&#xff09;涵盖常见疑问和注意事项&#xff1a; cn_windows_10_enterprise_ltsc_2019_x64_dvd_9c09ff24 下载链接&#xff1a;https://pan.quark.cn/s/c2c8f3cd18f1 1. 镜像文件来源与合法性 官方渠道&…...

死亡并不是走出生命 而是走出时间

目录 第一章 倒春寒 第二章 悖论与共生 第三章 坍缩与永恒 第四章 在时差里相爱 终章 你从未离开 第一章 倒春寒 2022年春天的扬州东关街&#xff0c;青衣在文昌阁古槐下调试着「时间胶囊」算法。这个能将人类记忆转化为数据流的程序&#xff0c;是他用三年时间对抗渐冻…...

Langchain中的表格解析:RAG 和表格的爱恨情仇

实现 RAG(Retrieval-Augmented Generation)是一个挑战,尤其是在有效解析和理解非结构化文档中的表格时。这在处理扫描文档或图像格式的文档时尤为困难。这些挑战至少包括以下三个方面: 1.表格的“叛逆期”:不准确的解析可能会破坏表格结构: 表格在文档里就像个叛逆的青少…...

STM32F103_LL库+寄存器学习笔记02 - 开启SysTick(滴答定时器)中断

导言 《STM32F103_LL库寄存器学习笔记01 - 梳理CubeMX生成的LL库最小的裸机系统框架》上一章节对CubeMX生成的最小系统框架进行梳理&#xff0c;在此工程的基础上&#xff0c;梳理SysTick&#xff08;滴答定时器&#xff09;中断是怎样开启的&#xff1f;为什么SysTick中断会自…...

AI小白的第七天:必要的数学知识(概率)

概率 Probability 1. 概率的定义 概率是一个介于 0 和 1 之间的数&#xff0c;表示某个事件发生的可能性&#xff1a; 0&#xff1a;事件不可能发生。1&#xff1a;事件必然发生。0 到 1 之间&#xff1a;事件发生的可能性大小。 例如&#xff0c;掷一枚公平的硬币&#xf…...

SVN常用命令

SVN常用命令 基本操作命令 • 检出代码&#xff08;Checkout&#xff09;&#xff1a;从SVN服务器获取代码到本地。 svn checkout [svn服务器url] [检出本地的path] 示例&#xff1a; svn checkout svn://47.106.183.193/helloworld ./ • 提交代码&#xff08;Commit&…...

23种设计模式中的策略模式

在策略模式定义了一系列算法或策略&#xff0c;并将每个算法封装在独立的类中&#xff0c;使得它们可以互相替换。通过使用策略模式&#xff0c;可以在运行时根据需要选择不同的算法&#xff0c;而不需要修改客户端代码。 策略模式&#xff1a;Strategy。指的是&#xff0c;定义…...

车载通信方案为何选择CAN/CANFD?

摘要 随着汽车电子技术的飞速发展&#xff0c;车载通信系统在车辆的智能化、网联化进程中扮演着至关重要的角色。控制器局域网络&#xff08;CAN&#xff09;及其扩展版本CANFD凭借其卓越的可靠性、高效的数据传输能力和强大的抗干扰特性&#xff0c;成为现代汽车通信架构的核心…...

有价值的面试问题

迅雷一面 都是c和网络问题 了解epoll吗&#xff1f;解释下水平触发和边缘触发&#xff0c;医院的叫号系统应该算哪一种 c类a有成员b&#xff0c;成员b调用了a的函数&#xff0c;但是a不小心把b的成员删除了&#xff0c;会发生什么&#xff0c;怎么解决 c类a有一个static的函数…...

深度学习|表示学习|多头注意力在计算时常见的张量维度变换总结|28

如是我闻&#xff1a; 以下是多头注意力&#xff08;Multi-Headed Attention&#xff09;在计算时常见的张量维度变换总结&#xff0c;帮助理解从输入到输出是如何一步步处理的。为了方便&#xff0c;令&#xff1a; B B B 表示 batch size&#xff08;批量大小&#xff09; S …...

Mysql内置函数篇

&#x1f3dd;️专栏&#xff1a;Mysql_猫咪-9527的博客-CSDN博客 &#x1f305;主页&#xff1a;猫咪-9527-CSDN博客 “欲穷千里目&#xff0c;更上一层楼。会当凌绝顶&#xff0c;一览众山小。” 目录 7.函数 7.1 日期函数 函数总&#xff1a;​编辑 获得当前日期 获得…...

使用事件监听器来处理并发环境中RabbitMQ的同步响应问题

RabbitListener 是 Spring AMQP 提供的核心注解&#xff0c;用于简化 RabbitMQ 消息监听器的创建。以下是对 RabbitListener(queues "balloonWords.queue") 的详细解析&#xff1a; 一、基础功能 队列监听 通过 queues 属性指定监听的队列名称&#xff08;如 "…...

基于Java的班级事务管理系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 随着世界经济信息化、全球化的到来和电子商务的飞速发展&#xff0c;推动了很多行业的改革。若想达到安全&#xff0c;快捷的目的&#xff0c;就需要拥有信息化的组织和管理模式&#xff0c;建立一套合理、畅通、高效的线上管理系统。当前的班级事务管理存在管理效率低下…...

Rviz 同时显示多个独立 URDF!解决双机械臂+底盘等场景(球体+方块实例演示)

视频讲解&#xff1a; Rviz 同时显示多个独立 URDF&#xff01;解决双机械臂底盘等场景&#xff08;球体方块实例演示&#xff09; 仓库地址&#xff1a;GitHub - LitchiCheng/ros2_package 有小伙伴留言说想看下同时使用多个独立的urdf如何配置&#xff0c;实际上这个场景是很…...

【C++】--- 类和对象(中)之日期类的实现

日期类的实现 1. 应该实现哪些默认成员函数 构造函数是需要自己来实现的&#xff0c;因为日期类的成员变量都是内置类型&#xff0c;是否初始化取决于编译器&#xff0c;这里可以给出一个带参全缺省的构造函数,由于日期类不需要申请资源&#xff0c;所有不用显式的实现析构函…...

kafka基础

一:消息队列(message queue [MQ]): 1.1消息队列解释:用来存储消息的队列 简单理解就是将需要的数据传输到队列里,队列可存可取,like 一个管道&#xff0c;但是与hdfs不同的是kafka作为临时存储 1.2消息队列中间件 消息队列中间件其实就是一个组件,简单例子就是用户对于服务器产…...

蓝桥杯第十届 特别的数

题目描述 小明对数位中含有 2、0、1、9 的数字很感兴趣&#xff08;不包括前导 0&#xff09;&#xff0c;在 1 到 40 中这样的数包括 1、2、9、10 至 32、39 和 40&#xff0c;共 28 个&#xff0c;他们的和是 574。 请问&#xff0c;在 1 到 n 中&#xff0c;所有这样的数的…...

fpga pcie

pcie reg 介绍 debug pcie issue using lspci & setpci 介绍了lspci & setpci用法&#xff0c; 以及fpga pcie调试过程中重点关注的reg Lspci -tv Sudo lspci -vvv -s 18:00.0 Sudo lspci -nvmms 18:00.0 setpci --dumpregs setpci -s 18:00.0 COMMAND #&#x…...

模数转换电路(A/D转换器)

A/D转换&#xff0c;是将输入的模拟电压量转换成相应的数字量。 A/D转换器的类型很多&#xff0c;按工作原理可分为直接转换型和间接转换型两大类。前者直接将模拟电压量转换成数字量&#xff0c;后者是先将模拟电压量转换成一个中间量&#xff0c;再将中间量转换成数字量。 …...

PAT乙级1007

常规解法 #include <iostream> using namespace std;// 判断一个数是否为素数的函数 bool isprime(int a) {// 遍历 2 到 sqrt(a) 之间的数&#xff0c;判断 a 是否能被它们整除for (int i 2; i * i < a; i) {if (a % i 0) // 如果能整除&#xff0c;说明 a 不是素…...

代码随想录刷题day52|(二叉树篇)106.从中序与后序遍历序列构造二叉树

目录 一、二叉树理论知识 二、构造二叉树思路 2.1 构造二叉树流程&#xff08;给定中序后序 2.2 整体步骤 2.3 递归思路 2.4 给定前序和后序 三、相关算法题目 四、易错点 一、二叉树理论知识 详见&#xff1a;代码随想录刷题day34|&#xff08;二叉树篇&#xff09;二…...

MTK平台 Android12-Android13 默认搜狗输入法

系统默认搜狗输入法功能实现 文章目录 需求&#xff1a;场景 参考资料需求实现内置搜狗输入法配置第三方apk .mk 和 搜狗安装包&#xff0c;不可卸载方式搜狗输入法module 配置到系统device.mk 中去 设置搜狗输入法为默认输入法给输入法授权&#xff0c;默认所有权限 总结思考 …...

vue3实现动态路由

文章目录 一、基础信息1.路由构成2.菜单配置表3.vue-router4方法 二、实现思路1.登录获取菜单配置表2.导航守卫3.添加动态路由4.渲染菜单5.退出登录删除动态路由 三、实现代码1.路由守卫2.基础路由文件3.添加动态路由逻辑4.待特殊处理路由配置表5.404类路由6.删除动态路由 场景…...

行为型设计模式

深入理解行为型设计模式&#xff1a;模板方法、观察者、责任链 设计模式是软件开发中解决常见问题的经典方案&#xff0c;而行为型设计模式尤其关注对象之间的职责分配与通信方式。本文将详细讲解模板方法模式、观察者模式和责任链模式。 一、模板方法模式&#xff08;Templat…...

【服务器环境安装指南-指定 cuda 版本】在 Ubuntu 22.04 上完成 cuda-toolkit 12.0 和 cudnn 12.x 的安装教程

0.引言 在深度学习和高性能计算领域&#xff0c;CUDA 和 cuDNN 是不可或缺的工具。为充分发挥硬件性能&#xff0c;我们需要在服务器环境中正确配置这些工具。然而&#xff0c;安装过程中可能会遇到诸多挑战&#xff0c;例如版本兼容性和环境变量设置等问题。本篇文章将以 Ubu…...

蓝桥杯第十届 数的分解

题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 把 2019 分解成 3 个各不相同的正整数之和&#xff0c;并且要求每个正整数都不包含数字 2 和 4&#xff0c;一共有多少种不同的分解方法&#xff1f; 注意交换 3 个…...

二叉搜索树

目录 概念 代码实现 成员 基本结构 查找 插入 删除 中序遍历 拷贝构造 赋值运算符重载 析构函数 递归实现 递归实现查找 递归实现插入 递归实现删除 概念 关于二叉树的基本结构已经进行过详细剖析&#xff0c;本篇博客将对一种特殊的二叉树进行分析。 二叉树&…...

Linux多线程详解

Linux多线程详解 一、Linux多线程概念1.1 什么是线程1.2 进程和线程1.3 进程的多个线程共享1.4 进程和线程的关系 二、Linux线程控制2.1 POSIX线程库2.2 线程创建2.3 获取线程ID pthread_self2.4 线程等待pthread_join2.5 线程终止2.6 线程栈 && pthread_t2.7 线程的局…...

攻防世界-web-1

Training-WWW-Robots 在URL后面加上/robots.txt 直接在URL后面添加/fl0g.php PHP2 他问我能不能登录这个网站&#xff0c;又因为考察php内容&#xff0c;在URL后面添加/index.php&#xff0c;无任何回显 试试/index.phps 分析一下代码&#xff0c;发现要用get方式上传idadmin,…...

笔记本+移动端维修全套教程

今天分享的是笔记本移动端维修全套教程&#xff08;免费视频资料大全&#xff09; 当自己手机或者电脑坏了&#xff0c;很多人都会想着去维修店铺修&#xff0c;但价格不透明&#xff0c;容易被坑&#xff0c;当自己了解一些之后&#xff0c;即使不会修&#xff0c;也可以对手…...

【STM32】知识点介绍二:GPIO引脚介绍

文章目录 一、概述二、GPIO的工作模式三、寄存器编程 一、概述 GPIO&#xff08;英语&#xff1a;General-purpose input/output&#xff09;,即通用I/O(输入/输出)端口&#xff0c;是STM32可控制的引脚。STM32芯片的GPIO引脚与外部设备连接起来&#xff0c;可实现与外部通讯、…...

【STM32】GPIO

目录 1、什么是GPIO2、什么是GPIO组3、GPIO的基本结构4、GPIO位结构5、GPIO八种工作模式6、GPIO相关寄存器1. 端口配置低寄存器GPIO[x]_CRL和端口配置高寄存器GPIO[x]_CRH, Config Register High和Config Register Low)2. 端口输入数据寄存器(GPIO[x]_IDR)3. 端口输出数据寄存器…...

鸿蒙移动应用开发--UI组件布局

实验要求&#xff1a; 制作一个B站视频卡片界面&#xff0c;大致如下图所示&#xff0c;要求应用到线性布局、层叠布局等相关课堂知识。背景图、logo及文本内容不限。 实验环境 &#xff1a;DevEco Studio 实验过程&#xff1a; 步骤1&#xff1a;创建项目 1. 在您的开发环境…...

[MySQL]MySQL数据库基础知识与操作

MySQL基础知识 为什么要有数据库&#xff1f; 文件存储的缺点 1.没有以某种特定的数据格式存储数据&#xff0c;查找不方便&#xff0c;只能遍历2.安全性&#xff1a;数据误操作后不能回滚3.每次操作数据都要用户自己操作4.数据量大的时候&#xff0c;操作的成本很高 创建一…...

卡诺图化简法的原理

引子 若两个最小项只有一个因子不同&#xff0c;则称这两个最小项具有相邻性。 例如&#xff0c; A ′ B C ′ ABC A′BC′和 A B C ABC ABC两个最小项仅第一个因子不同&#xff0c;所以它们具有相邻性。这两个最小项相加时定能合并成一项并将一对不同的因子消去 A ′ B C ′…...

从零开始:使用Luatools工具高效烧录Air780EPM核心板项目的完整指南

本文将深入讲解如何使用Luatools工具烧录一个具体的项目到Air780EPM开发板中。如何使用官方推荐的Luatools工具&#xff08;一款跨平台、命令行驱动的烧录利器&#xff09;&#xff0c;通过“环境配置→硬件连接→参数设置→一键烧录”四大步骤&#xff0c;帮助用户实现Air780E…...

探秘Transformer系列之(18)--- FlashAttention

探秘Transformer系列之&#xff08;18&#xff09;— FlashAttention 文章目录 0x00 概述0.1 问题0.2 其它解决方案0.3 Flash Attention 0x01 背景知识1.1 GPU相关概念硬件概念运行单元内存 软件概念运行模式线程模型Grid & DeviceBlock & SMThread & SPThread &am…...

VUE2导出el-table数据为excel并且按字段分多个sheet

首先在根目录下建一个文件夹export用来存储export.js import * as XLSX from xlsxfunction autoWidthFunc(ws, data) {// 设置每列的最大宽度const colWidth data.map(row > row.map(val > {var reg new RegExp([\\u4E00-\\u9FFF], g) // 检测字符串是否包含汉字if (v…...

Android面试总结之Android RecyclerView:从基础机制到缓存优化

引言 在 Android 开发中&#xff0c;RecyclerView是高效展示列表数据的核心组件。其强大的性能源于独特的视图复用机制和四级缓存体系。本文将结合源码与示例&#xff0c;带你深入理解RecyclerView的工作原理与优化策略。 核心组件 RecyclerView&#xff1a;作为容器视图&am…...

【C#语言】C#文件操作实战:动态路径处理与安全写入

文章目录 ⭐前言⭐一、场景痛点⭐二、完整实现代码⭐三、关键技术解析&#x1f31f;1、动态路径处理&#x1f31f;2、智能目录创建&#x1f31f;3、安全的文件写入 ⭐四、进阶扩展方案&#x1f31f;1、用户自定义路径选择&#x1f31f;2、异常处理增强&#x1f31f;3、异步写入…...

react中 useEffect和useLayoutEffect的区别

useEffect 和 useLayoutEffect 都是 React 中用于处理副作用的 Hook&#xff0c;但它们在执行时机和用途上有一些关键区别。理解这些区别可以帮助你更好地选择适合的 Hook 来实现特定的功能。 1. 执行时机 useEffect&#xff1a; 异步执行&#xff1a;useEffect 是在组件渲染完…...

TDengine 中的系统信息统计

简介 TDengine 3.0 版本开始提供一个内置数据库 performance_schema&#xff0c;Performance_Schema 数据库中存储了系统中的各种统计信息&#xff0c;包括存储及性能有关统计数据。本节详细介绍其中的表和表结构。 PERF_APP 提供接入集群的应用&#xff08;客户端&#xff…...

C++可变参数

可变参数C风格的可变参数C风格可变参数的使用 C11可变参数模板递归展开参数包参数列表展开折叠表达式 STL中的emplace插入接口 可变参数 C风格的可变参数 可变参数是一种语言特性&#xff0c;可以在函数声明中使用省略号...来表示函数接受可变数量的参数。 例如典型的printf…...

建造者模式 (Builder Pattern)

建造者模式 (Builder Pattern) 是一种创建型设计模式,它将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。 一、基础 1.1 意图 将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。 1.2 适用场景 当创建复杂对象的算法应该…...

Thales靶机攻略

1.下载导入VBox&#xff0c;并启动靶机 靶机地址&#xff1a;https://download.vulnhub.com/thales/Thales.zip 解压后&#xff0c;在VBox中导入虚拟电脑。包含所有网卡的MAC地址。 导入完成&#xff0c;设置网卡模式为仅主机网络。开启靶机。 kali网卡更改为桥接模式。点击工…...

【redis】哨兵:搭建主从/哨兵节点详解和细节

文章目录 编排步骤搭建主从节点创建容器启动容器 搭建哨兵节点创建容器哨兵节点配置文件配置节点启动容器 主从/哨兵节点连入同一个局域网 编排步骤 分为两组 yml&#xff0c;先后启动 我们其实也可以用于一个 yml 文件&#xff0c;直接启动 6 个容器&#xff0c;但是&#x…...

零基础上手Python数据分析 (9):DataFrame 数据读取与写入 - 让数据自由穿梭

回顾一下,上篇博客我们学习了 Pandas 的核心数据结构 Series 和 DataFrame。 DataFrame 作为 Pandas 的 “王牌” 数据结构,是进行数据分析的基石。 但 DataFrame 的强大功能,还需要建立在 数据输入 (Input) 和 数据输出 (Output) 的基础上。 数据从哪里来? 分析结果又如何…...

Emacs 折腾日记(十九)——配置输入法和vim操作方式

上一篇文章中&#xff0c;我们将Emacs变得稍微好看了点。换成了自己喜欢的主题和颜色&#xff0c;这样每天用起来也比较养眼&#xff0c;不会特别排斥。本篇文章的主要任务就是配置输入法方便输入中文以及将vim的操作模式搬到Emacs中。进一步提到Emacs的可用性 配置中文输入法…...

Docker 镜像构建与优化

一、Dockerfile 构建镜像 1.1.拉取所需镜像 首先 docker pull 拉取一个 centos7 的镜像。 docker pull centos:7 下载 nginx 源码包。 官网&#xff1a;nginx: download wget https://nginx.org/download/nginx-1.26.3.tar.gz 1.2.解决 CentOS 7 安装源问题 因为原本的 …...