• ThreadLocal线程变量


    目录

    ThreadLocal是什么?

    ThreadLocal实现原理分析

    ThreadLocal内存泄漏问题


    ThreadLocal是什么?

               ThreadLocal 使得我们可以创建线程私有的变量, 这个变量相对于其他线程来说是不可见的,ThreadLocal为变量在每个线程中都创建了一个副本 , 每个线程可以访问自己私有的线程变量,代码示例如下 : 

    1. public class ThreadLocalDemo {
    2. //创建一个ThreadLocal对象,用来为每个线程会复制保存一份变量,实现线程封闭
    3. private static ThreadLocal<Integer> localNum = new ThreadLocal<Integer>(){
    4. @Override
    5. protected Integer initialValue() {
    6. return 1;
    7. }
    8. };
    9. public static void main(String[] args) {
    10. //线程0
    11. new Thread(){
    12. @Override
    13. public void run() {
    14. localNum.set(1);
    15. try {
    16. Thread.sleep(2000);
    17. } catch (InterruptedException e) {
    18. e.printStackTrace();
    19. }
    20. localNum.set(localNum.get()+10);
    21. System.out.println(Thread.currentThread().getName()+":"+localNum.get());//11
    22. }
    23. }.start();
    24. //线程1
    25. new Thread(){
    26. @Override
    27. public void run() {
    28. localNum.set(3);
    29. try {
    30. Thread.sleep(2000);
    31. } catch (InterruptedException e) {
    32. e.printStackTrace();
    33. }
    34. localNum.set(localNum.get()+20);
    35. System.out.println(Thread.currentThread().getName()+":"+localNum.get());//23
    36. }
    37. }.start();
    38. System.out.println(Thread.currentThread().getName()+":"+localNum.get());//0
    39. }
    40. }

             如上所述, 算上main线程与新建的两个线程 ,总共三个线程 , 每个线程都包含自己的私有变量,此处我们设置值1 , set() 和 get() 方法用来设置值和获得值, 执行结果如下 : 

    ThreadLocal实现原理分析

                ThreadLocal是一个泛型类 , 可以接受任何类型的对象 , 其内部维护了一个ThreadLocalMap 的静态内部类,  我们使用的 get(), set()等其实都来自这个类, 每次都会为当前线程创建一个ThreadLocalMap对象, 用来记录私有的值

    先看 set() 方法

    1. public void set(T value) {
    2. //拿到当前线程
    3. Thread t = Thread.currentThread();
    4. //拿到当前线程map
    5. ThreadLocalMap map = getMap(t);
    6. if (map != null)
    7. //存在设置值
    8. map.set(this, value);
    9. else
    10. //不存在则创建
    11. createMap(t, value);
    12. }
    1. void createMap(Thread t, T firstValue) {
    2. //threadLocals属性即为此map
    3. t.threadLocals = new ThreadLocalMap(this, firstValue);
    4. }

    接着是get() 方法

    1. public T get() {
    2. //拿到当前线程
    3. Thread t = Thread.currentThread();
    4. //拿到当前线程对应的map
    5. ThreadLocalMap map = getMap(t);
    6. //如果已有map
    7. if (map != null) {
    8. //取值操作, 拿到对应的Entry
    9. ThreadLocalMap.Entry e = map.getEntry(this);
    10. if (e != null) {
    11. @SuppressWarnings("unchecked")
    12. T result = (T)e.value;
    13. return result;
    14. }
    15. }
    16. //没有map, 则去创建初始化一个map
    17. return setInitialValue();
    18. }
    1. private T setInitialValue() {
    2. //initialValue()方法返回的value为null
    3. T value = initialValue();
    4. //拿到当前线程去创建对应的map
    5. Thread t = Thread.currentThread();
    6. ThreadLocalMap map = getMap(t);
    7. if (map != null)
    8. map.set(this, value);
    9. else
    10. createMap(t, value);
    11. return value;
    12. }

            ThreadLocal可以理解为对ThreadLocalMap的封装

    ThreadLocal内存泄漏问题

             在ThreadLocalMap中 , 使用 ThreadLocal 的弱引用作为 key 

           这样的话, 如果一个ThreadLocal不存在外部强引用时, 那么key注定要被GC回收 , 这样导致ThreadLocalMap 中key为null , 而value还存在着强引用链

           一个线程可以同时拥有多个ThreadLocal, 如果作为弱引用的key被回收后, value还不能被回收,那么这就导致此ThreadLocal的生命周期和此线程是一样长的(因为线程执行完毕后此value的强引用链才会断), 如果线程一直不结束, 堆积的value也一直无法被回收, 那么就会产生内存泄漏问题

    这里解决问题的方式是 : 每次使用完ThreadLocal后都调用它的remove()方法清除数据

    1. public void remove() {
    2. ThreadLocalMap m = getMap(Thread.currentThread());
    3. if (m != null)
    4. m.remove(this);
    5. }

    这里我们再来看一下key作为强弱引用的区别

            如果key作为强引用, 那么它的生命周期和线程一样长,存在稳定的强引用链,无法被回收,产生内存泄漏问题, 而如果作为弱引用, GC则会自动的去回收它们, 在后续的remove()方法中也可以更好的去回收value , 所以我们一般将ThreadLocal设计成 private static 的, 在使用完后用remove()方法去手动删除它们


     

  • 相关阅读:
    使用C语言实现单链表(不带头节点)
    【node进阶】深入浅出前后端身份验证(下)---JWT
    想要快速增肌?肌酸可以帮你高效获得肌肉和力量
    Go语言Gin框架前后端分离项目开发工程化实例
    CSDN博客编写的相关操作
    面向对象特性之继承
    MoeCTF2023web
    项目实践《小说网站数据爬取》
    【数据结构与算法】LinkedList与链表(上)
    【uvm】How to write uvm sequence
  • 原文地址:https://blog.csdn.net/xx12321q/article/details/124986995