ThreadLocal 详解

用处

为每一个线程提供一个独立的变量副本,即多线程调用变量只调用自己的变量副本。

场景,跨方法进行参数调用时,案例

  1. 在开启事务时,对连接开启事务,执行 insert() 方法时却是随机获取的连接执行,此时可以将连接绑定 ThreadLocal
  2. 链路追踪,即一个请求在多个微服务中调用时,通过 traceId 来判断,即跨多个服务调用时,可以绑定 ThreadLocal,将 id 一直下传

使用

  • set(),设置当前线程局部变量值
  • get(),获取当前线程局部变量值
  • remove(),删除当前线程局部变量
  • initialValue(),返回初始值,提供给子类覆盖设计。只有在第一次 get() 或者 set() 中调用,且只调用一次,ThreadLocal 初始为 null

ThreadMap 实现

本质是在线程里存一个 ThreadLocalMap,将 threadLocal 对象 -value 值作为 Entry 存入其中,threadLocal 对象调用 get()
方法时,实则是获取当前线程的 ThreadLocalMap,后用 threadLocal 对象获取 value

Entry 数组 + 开放定值法,即 hash 冲突时则没用链表

hash 冲突算法

  • 开放定值法,冲突后按照一定算法查找一个空位置,此时有线性探测 (依次后移查找),二次探测 (依次后移时,按照平方后移查找)
    ,伪随机 (则随机产生一个增量位移量)
  • 链地址,即加链表
  • 再哈希法,通过不同哈希函数,冲突时依次使用哈希函数直到冲突不再产生
  • 公共溢出区,将哈希表分为基本表和溢出表,只要冲突则放入溢出表

导致 JVM 内存泄露原因

image-20230310153942746

这种情况发生的关键条件是,线程池中工作线程一直未结束,而任务的局部成员变量赋值给了线程的 ThreadLocalMap
中,任务执行完成后本应该回收,但线程一直运行,其 ThreadLocalMap 对该变量有强引用,导致 ThreadLocalMap 中的 Entry
会一直保留,且随着该任务的重复执行而逐渐累加,

即 GC 时会将只有弱引用的对象回收



title: CAS & Atomic


原子性可以用锁机制,但过于笨重,粒度较大。

对应计数器等需求,Java 提供了 Atomic 的原子操作类

CAS 三大问题

ABA

添加版本号

java 解决方式

  • AtomicMarkableReference,记录是否修改过
  • AtomicStampedReference,记录修改次数

循环时间长

大量线程竞争强时,使用 syn 比 CAS 更快

只能保证一个共享变量的原子操作

包装到同一个原子对象中

LongAdder 类

写热点的分散,将单值,变为一个数组,让每个线程读取不同值