ToB企服应用市场:ToB评测及商务社交产业平台

标题: ThreadLocal入门条记 [打印本页]

作者: 莫张周刘王    时间: 2024-5-19 21:31
标题: ThreadLocal入门条记
ThreadLocal入门条记

最近学习小傅哥的面经手册,学习到ThreadLocal,这里做个条记加深印象,也方便日后复习。
ThreadLocal是除了加锁这种同步方式之外的一种规避多线程访问出现线程不安全的方法,它的核心思想是:共享变量在每个线程都有一个副本,每个线程操作的都是自己的副本,对另外的线程没有影响。
一、ThreadLocal简单使用
  1. public class ThreadLocalDemo {
  2.     static ThreadLocal<String> localVar = new ThreadLocal<>();
  3.     static void print(String str){
  4.         //打印当前线程中本地内存中本地变量的值
  5.         System.out.println(str + " : " + localVar.get());
  6.         //清除本地内存中的本地变量
  7.         localVar.remove();
  8.     }
  9.     public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
  10.         Thread thread1 = new Thread(new Runnable() {
  11.             @Override
  12.             public void run() {
  13.                 localVar.set("localVar1");
  14.                 print("localVar1");
  15.                 //打印本地变量
  16.                 System.out.println("after remove : " + localVar.get());
  17.             }
  18.         });
  19.         Thread thread2 = new Thread(new Runnable() {
  20.             @Override
  21.             public void run() {
  22.                 localVar.set("localVar2");
  23.                 print("localVar2");
  24.                 //打印本地变量
  25.                 System.out.println("after remove : " + localVar.get());
  26.             }
  27.         });
  28.         thread1.start();
  29.         thread2.start();
  30.     }
  31. }
复制代码
打印结果:
  1. localVar1 : localVar1
  2. after remove : null
  3. localVar2 : localVar2
  4. after remove : null
复制代码
一、ThreadLocal结构

Thread类中有ThreadLocalMap类的成员变量threadLocals,这变量由ThreadLoacl类维护。

ThreadLocalMapThreadLocal类的静态内部类,内部有一个Entry静态内部类,继承了WeakReference。所以存储在Entry中的ThreadLocal键是弱引用。
弱引用:当一个对象仅仅被weak reference指向, 而没有任何其他strong reference指向的时间, 假如GC运行, 那么这个对象就会被回收。

ThreadLocalMap内部还界说了一个成员变量Entry[] table。

所以ThreadLocal的整体结构:

【图片泉源】:ThreadLocal一个线程只能存放一个变量吗?想存多个怎么搞? - 苏三说技能的回答 - 知乎

【图片泉源】:ThreadLocal一个线程只能存放一个变量吗?想存多个怎么搞? - 苏三说技能的回答 - 知乎
二、怎样存放元素

ThreadLocal 存放数据的底层数据结构:

图片泉源:面经手册 · 第12篇《口试官,ThreadLocal 你要这么问,我就挂了!》 | 小傅哥 bugstack 虫洞栈
ThreadLocal 使用的是斐波那契(Fibonacci)散列法 + 开发寻址存储数据到数组结构Entry[] table中。
ThreadLocale类 set(T value)源码流程

流程图:(这图中拉链法,感觉该改为开放寻址法的线性探测?)
深入理解Hash表:拉链法与开放寻址法的比较-百度开发者中央

图片泉源:面经手册 · 第12篇《口试官,ThreadLocal 你要这么问,我就挂了!》 | 小傅哥 bugstack 虫洞栈
三、扩容机制

ThreadLocalMap类 set(ThreadLocal key, Object value)源码的最后一段代码:
  1. int sz = ++size;
  2. if (!cleanSomeSlots(i, sz) && sz >= threshold)
  3.     rehash();
复制代码
这段代码的源码流程:
真正的进行扩容的操作resize():
resize()源码如下:

四、怎样获取元素

ThreadLocal类 get()方法源码流程:
五、清理元素

探测式清理[expungeStaleEntry]

探测式清理,是以当前遇到的 GC 元素开始,向后不停的清理。直到遇到 null 为止,才停止 rehash 计算Rehash until we encounter null, for(...; (e = tab) != null; ...)。
源码流程:
for循环中:
ThreadLoaclMap类 expungeStaleEntry(int): int源码如下:

启发式清理[cleanSomeSlots]

启发式清理,有这么一段注释,大概意思是;试探的扫描一些单位格,寻找过期元素,也就是被垃圾回收的元素。当添加新元素或删除另一个过期元素时,将调用此函数。它执行对数扫描次数,作为不扫描(快速但保留垃圾)和与元素数量成比例的扫描次数之间的平衡,这将找到全部垃圾,但会导致一些插入花费O(n)时间。
while 循环中不停的右移进行寻找需要被清理的过期元素,最终都会使用 expungeStaleEntry 进行处理,这里还包罗元素的移位。
ThreadLoaclMap类 cleanSomeSlots(int, int): boolean源码如下:

问题:

参考资料

ThreadLocal一个线程只能存放一个变量吗?想存多个怎么搞? - 苏三说技能的回答 - 知乎
面经手册 · 第12篇《口试官,ThreadLocal 你要这么问,我就挂了!》 | 小傅哥 bugstack 虫洞栈
Java中的ThreadLocal详解 - 夏末秋涼 - 博客园

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4