1.InheritableThreadLocal源码剖析
2.ThreadLocal及弱引用
3.通过transmittable-thread-local源码理解线程池线程本地变量传递的码详原理
4.Netty源码解析 -- FastThreadLocal与HashedWheelTimer
5.ThreadLocal面试题自己整理
InheritableThreadLocal源码剖析
InheritableThreadLocal是Java中用于在多线程环境共享数据的工具,它允许子线程继承父线程的码详值,从而避免了线程间数据同步的码详复杂性。与ThreadLocal不同,码详InheritableThreadLocal实现了数据的码详继承机制,确保了数据在父线程到子线程间的码详品牌售后溯源码顺利传递。这使得在使用线程池或其他线程管理技术时,码详应用程序能够保持数据的码详一致性和完整性。
InheritableThreadLocal提供了一种从父线程到子线程的码详数据传递方式,它通过在Thread类中引入了inheritableThreadLocals字段来实现这一功能。码详这一字段是码详一个ThreadLocalMap类型的对象,专门用于存储InheritableThreadLocal实例。码详这意味着当创建子线程时,码详它会自动接收并继承父线程的码详值。
实现这一特性,码详InheritableThreadLocal主要通过三个关键方法:set、get、remove。它们与ThreadLocal的同名方法相似,但操作的内部数据结构有所不同。InheritableThreadLocal的set、get、remove方法会通过获取inheritableThreadLocals字段中的ThreadLocalMap对象来进行操作,而ThreadLocal则通过操作threadLocals字段。
为了验证InheritableThreadLocal的继承机制,可以通过在父线程中设置InheritableThreadLocal的值,然后在子线程中尝试获取该值来观察结果。实验证明,子线程能够成功获取到父线程设置的值,证明了InheritableThreadLocal的仿独特论坛源码继承功能。
在使用InheritableThreadLocal时,需要注意的是它的内存管理。一旦线程创建了InheritableThreadLocal实例,它会一直保留在所有后代线程中,直到显式调用remove方法或线程结束。因此,在资源管理和内存控制上,开发者需要特别注意,以防止潜在的内存泄漏问题。
总之,InheritableThreadLocal通过在Thread类中引入专门的数据结构和方法来实现其独特的继承机制,简化了多线程编程中数据共享和管理的复杂性。然而,其使用需要谨慎,以避免不必要的内存占用和潜在的内存泄漏风险。
ThreadLocal及弱引用
Thread中持有一个ThreadLocalMap对象,默认为null;ThreadLocalMap是ThreadLocal的静态内部类,本质上是一个容器,ThreadLocal更像是ThreadLocalMap的工具类,提供了操作ThreadLocalMap的方法,如get()和set()。
测试代码显示,main线程与Thread-0线程调用set()方法设置不同值,但最终main线程前后打印结果一致,表明两者是线程隔离的,变量相互独立。
ThreadLocal源码跟踪揭示其核心在于与ThreadLocalMap的交互,重点是get()和set()方法。
set源码跟踪解析如下:线程创建时,源码可以更改吗threadLocals为null;调用getMap(currentThread)得到当前线程的threadLocals,若未绑定实例则创建一个,并使Thread中的threadLocals指向新创建的ThreadLocalMap实例。ThreadLocalMap内部为哈希表,大小默认,插入元素时,根据key算出槽位,将键值对插入。
get源码讨论了未set值的情况:若ThreadLocalMap未初始化,则先调用setInitialValue()初始化,返回null。若已初始化但未得到对应结果,则插入{ this ThreadLocal : initialValue}键值对,返回initialValue。这样下次get时能找到值。
ThreadLocal与弱引用关系:ThreadLocalMap的key使用弱引用,当执行map.getEntry(threadLocal)或map.getKey(threadLocal)时,会找到对应的键值对。弱引用保证在GC到来时可回收key,但value可能仍存在,直到线程结束。设计中考虑了这种情况,get、set、remove时清除null值。
弱引用解决内存泄露问题,但在某些情况下可能引起内存泄露。ThreadLocalMap的设计通过清除null值来防止这种情况,确保资源有效管理。跳转导航源码
相关资源提供进一步理解ThreadLocal与弱引用的深入细节。
通过transmittable-thread-local源码理解线程池线程本地变量传递的原理
最近几周,我投入了大量的时间和精力,完成了UCloud服务和中间件迁移至阿里云的工作,因此没有空闲时间撰写文章。不过,回忆起很早之前对ThreadLocal源码的分析,其中提到了ThreadLocal存在向预先创建的线程中传递变量的局限性。恰好,我的一位前同事,HSBC的技术大牛,提到了团队引入了transmittable-thread-local(TTL)来解决此问题。借此机会,我深入分析了TTL源码,本文将全面分析ThreadLocal和InheritableThreadLocal的局限性,并深入探讨TTL整套框架的实现。如有对线程池和ThreadLocal不熟悉的读者,建议先阅读相关前置文章,本篇文章行文较为干硬,字数接近5万字,希望读者耐心阅读。
在Java中,没有直接的API允许子线程获取父线程的实例。获取父线程实例通常需要通过静态本地方法Thread#currentThread()。同样,为了在子线程中传递共享变量,也常采用类似的方法。然而,这种方式会导致硬编码问题,生鲜系统源码下载限制了方法的复用性和灵活性。为了解决这一问题,线程本地变量Thread Local应运而生,其基本原理是通过线程实例访问ThreadLocal.ThreadLocalMap来实现变量的存储与传递。
ThreadLocal与InheritableThreadLocal之间的区别主要在于控制ThreadLocal.ThreadLocalMap的创建时机和线程实例中对应的属性获取方式。通过分析源码,可以清楚地看到它们之间的联系与区别。对于不熟悉概念的读者,可以尝试通过自定义实现来理解其中的原理与关系。
ThreadLocal和InheritableThreadLocal的最大局限性在于无法为预先创建的线程实例传递变量。泛线程池Executor体系、TimerTask和ForkJoinPool等通常会预先创建线程,因此无法在这些场景中使用ThreadLocal和InheritableThreadLocal来传递变量。
TTL提供了更灵活的解决方案,它通过委托机制(代理模式)实现了变量的传递。委托可以基于Micrometer统计任务执行时间并上报至Prometheus,然后通过Grafana进行监控展示。此外,TTL通过字节码增强技术(使用ASM或Javassist等工具)实现了类加载时期替换Runnable、Callable等接口的实现,从而实现了无感知的增强功能。TTL还使用了模板方法模式来实现核心逻辑。
TTL框架的核心类TransmittableThreadLocal继承自InheritableThreadLocal,通过全局静态变量holder来管理所有TransmittableThreadLocal实例。holder实际上是一个InheritableThreadLocal,用于存储所有线程本地变量的映射,实现变量的全局共享。disableIgnoreNullValueSemantics属性的设置可以影响NULL值的处理方式,影响TTL实例的行为。
发射器Transmitter是TransmittableThreadLocal的一个公有静态类,提供传输TransmittableThreadLocal实例和注册当前线程变量至其他线程的功能。通过Transmitter的静态方法,可以实现捕获、重放和复原线程本地变量的功能。
TTL通过TtlRunnable类实现了任务的封装,确保在执行任务时能够捕获和传递线程本地变量。在任务执行前后,通过capture和restore方法捕获和重放变量,实现异步执行时上下文的传递。
启用TTL的Agent模块需要通过Java启动参数添加javaagent来激活字节码增强功能。TTL通过Instrumentation回调激发ClassFileTransformer,实现目标类的字节码增强,从而在执行任务时自动完成上下文的捕捉和传递。
TTL框架提供了一种高效、灵活的方式来解决线程池中线程复用时上下文传递的问题。通过委托机制和字节码增强技术,TTL实现了无入侵地提供线程本地变量传递功能。如果您在业务代码中遇到异步执行时上下文传递的问题,TTL库是一个值得考虑的解决方案。
Netty源码解析 -- FastThreadLocal与HashedWheelTimer
Netty源码分析系列文章接近尾声,本文深入解析FastThreadLocal与HashedWheelTimer。基于Netty 4.1.版本。 FastThreadLocal简介: FastThreadLocal与FastThreadLocalThread协同工作。FastThreadLocalThread继承自Thread类,内部封装一个InternalThreadLocalMap,该map只能用于当前线程,存放了所有FastThreadLocal对应的值。每个FastThreadLocal拥有一个index,用于定位InternalThreadLocalMap中的值。获取值时,首先检查当前线程是否为FastThreadLocalThread,如果不是,则从UnpaddedInternalThreadLocalMap.slowThreadLocalMap获取InternalThreadLocalMap,这实际上回退到使用ThreadLocal。 FastThreadLocal获取值步骤: #1 获取当前线程的InternalThreadLocalMap,如果是FastThreadLocalThread则直接获取,否则通过UnpaddedInternalThreadLocalMap.slowThreadLocalMap获取。#2 通过每个FastThreadLocal的index,获取InternalThreadLocalMap中的值。
#3 若找不到值,则调用initialize方法构建新对象。
FastThreadLocal特点: FastThreadLocal无需使用hash算法,通过下标直接获取值,复杂度为log(1),性能非常高效。 HashedWheelTimer介绍: HashedWheelTimer是Netty提供的时间轮调度器,用于高效管理各种延时任务。时间轮是一种批量化任务调度模型,能够充分利用线程资源。简单说,就是将任务按照时间间隔存放在环形队列中,执行线程定时执行队列中的任务。 例如,环形队列有个格子,执行线程每秒移动一个格子,则每轮可存放1分钟内的任务。任务执行逻辑如下:给定两个任务task1(秒后执行)、task2(2分秒后执行),当前执行线程位于第6格子。那么,task1将放到+6=格,轮数为0;task2放到+6=格,轮数为2。执行线程将执行当前格子轮数为0的任务,并将其他任务轮数减1。 HashedWheelTimer的缺点: 时间轮调度器的时间精度受限于执行线程的移动速度。例如,每秒移动一个格子,则调度精度小于一秒的任务无法准时调用。 HashedWheelTimer关键字段: 添加延迟任务时,使用HashedWheelTimer#newTimeout方法,如果HashedWheelTimer未启动,则启动HashedWheelTimer。启动后,构建HashedWheelTimeout并添加到timeouts集合。 HashedWheelTimer运行流程: 启动后阻塞HashedWheelTimer线程,直到Worker线程启动完成。计算下一格子开始执行的时间,然后睡眠到下次格子开始执行时间。获取tick对应的格子索引,处理已到期任务,移动到下一个格子。当HashedWheelTimer停止时,取消任务并停止时间轮。 HashedWheelTimer性能比较: HashedWheelTimer新增任务复杂度为O(1),优于使用堆维护任务的ScheduledExecutorService,适合处理大量任务。然而,当任务较少或无任务时,HashedWheelTimer的执行线程需要不断移动,造成性能消耗。另外,使用同一个线程调用和执行任务,某些任务执行时间过久会影响后续任务执行。为避免这种情况,可在任务中使用额外线程执行逻辑。如果任务过多,可能导致任务长期滞留在timeouts中而不能及时执行。 本文深入剖析FastThreadLocal与HashedWheelTimer的实现细节,旨在提供全面的技术洞察与实战经验。希望对您理解Netty源码与时间轮调度器有帮助。关注微信公众号,获取更多Netty源码解析与技术分享。ThreadLocal面试题自己整理
ThreadLocal是Java提供的一种线程内部局部变量,能保证各个线程变量相对独立于其他线程中的变量。在多线程环境下访问时,ThreadLocal实例通常为private static类型,关联线程和线程上下文,有助于在不同线程间实现隔离。 其典型应用场景包括: Spring框架利用ThreadLocal保证单个线程中的数据库操作使用同一连接,避免了频繁创建连接的开销。同时,通过传播级别巧妙管理事务切换,优化了事务处理流程。 在需要传递上下文(如用户身份、任务信息等)的场景中,ThreadLocal提供了一种简便且高效的方式,避免了参数传递的复杂性。 ThreadLocal的源码分析包括: set方法:用于设置线程局部变量的值。 get方法:用于获取线程局部变量的值。 ThreadLocalMap的底层结构是数组,而非通常的哈希表实现。每个ThreadLocal对象都有一个threadLocalHashCode,用于定位到数组中的特定位置进行存储。采用开放地址法解决哈希冲突,而非链表。 如果想在父子进程间共享ThreadLocal中的值,可通过使用InheritableThreadLocal。在主线程中创建InheritableThreadLocal实例,并在子线程中获取该实例的值。 ThreadLocal不会导致内存泄漏,因为ThreadLocalMap中的Entry设计为弱引用。弱引用允许垃圾回收器在必要时回收引用的对象,从而避免因持有引用导致的内存泄漏问题。