首页 > 其他分享 >ThreadLocal

ThreadLocal

时间:2023-04-08 14:23:44浏览次数:37  
标签:map ThreadLocalMap value ThreadLocal 线程 null

什么是ThreadLocal

  • ThreadLocal提供了线程局部变量. 这些变量和正常的变量不同,因为每一个线程在访问ThreadLocal实例的时候 都有自己独立的 变量副本.
  • ThreadLocal实例通常是类的私有静态字段,使用它的目的是希望将状态(用户ID、事务ID) 与线程关联起来
  • 通俗易懂: 实现每一个线程都有自己的专属本地变量副本

ThreadLocal能解决什么问题

  • 我们知道解决线程安全问题,要么用synchronzied、ReentrantLock、或者用原子类, 这类解决方法说白了都是通过加锁的方式
  • 而 ThreadLocal则是 每个线程都存有自己的本地变量副本,各玩各的 互不打扰

ThreadLocal需要注意的点

  • 使用完ThreadLocal一定要手动的remove

    • 如果是使用线程池处理业务,会存在线程复用的情况,上一段业务处理的结果没有清空 会影响到下一段业务,造成数据错乱,严重的会造成内存溢出
    • 代码
      • 下面的submit执行逻辑 如果没有手动remove,就会导致累加操作无限下去,
      class Number {
          ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
      
          public void add(){
              threadLocal.set(threadLocal.get() + 1);
          }
      }
      
      public class ThreadLocal003 {
      
          public static void main(String[] args) {
              ExecutorService executorService = Executors.newFixedThreadPool(3);
      
              Number number = new Number();
      
              for (int i = 0;i < 10;i++){
                  executorService.submit(() -> {
                      try {
                          Integer beforeInt = number.threadLocal.get();
                          number.add();
                          Integer afterInt = number.threadLocal.get();
                          System.out.println(Thread.currentThread().getName() + "\t\t beforeInt: " + beforeInt + "---afterInt: " + afterInt);
                      } finally {
                          number.threadLocal.remove();
                      }
                  });
              }
      
          }
      }
      
  • 为什么ThreadLocal会有内存泄露的风险

    • 什么是内存泄漏

      • 不再会被使用的对象或者变量占用的内存不能被回收
    • 弱引用修饰ThreadLocal 保证了 Entry对象中的key 指向ThreadLocal对象能被及时回收,但是 v指向的对象是需要调用set get 方法发现key是null才会回收整个Entry, 因此弱引用不能百分比解决内存泄漏问题.

      • 我们不使用某个ThreadLocal对象后,一定要调用remove方法删除它
      • 尤其是线程池中,不仅仅是内存泄漏的问题,因为线程池中的线程是复用的,意味着线程的ThreadLocalMap对象也是重复使用的,如果不手动调用remove方法,那么后面的线程就有可能获取到上一个线程遗留下来的value值,造成bug
  • 为什么ThreadLocalMap 里的entry对象 需要继承WeakReference(弱引用)

    • 栈 强引用指向ThreadLocal 对象, ThreadLocalMap 引用指向ThreadLocal对象
    • 如果是entry对象是强引用 指向ThreadLocal, 一旦栈的强引用被回收后, entry对象key指向ThreadLocal不能被gc回收,造成内存泄漏
    • 如果是弱引用,就会减少内存泄漏的问题

ThreadLocal源码分析

  • Thread、ThreadLocal、ThreadLocalMap之间的关系

    • ThreadLocalMap是ThreadLocal的静态内部类
    • Thread类中存在 ThreadLocalMap属性
      image.png
    • ThreadLocalMap 是一个保存ThreadLocal对象的map(ThreadLocal作为key)
  • 属性说明

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    
    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    
  • 公共方法

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    
    private T setInitialValue() {
        
        // 初始值是null
        T value = initialValue();
        Thread t = Thread.currentThread();
        
        // 获取map
        // map如果是null 则创建map,key是ThreadLocal value是 初始值value 长度大小默认是18
        // map不是null,则 put进去
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
    
  • get方法

    public T get() {
        // 获取到当前线程
        Thread t = Thread.currentThread();
        
        // 当前线程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        // 初始化
        return setInitialValue();
    }
    

总结、

  • ThreadLocal并不解决线程间共享数据的问题
  • 适用于变量在线程间隔离且在方法间共享的场景
  • ThreadLocal在各个线程创建了独立的变量副本避免了线程安全问题
  • ThreadLocalMap的Entry对 ThreadLocal的引用为弱引用, 避免了ThreadLocal无法被回收的问题
  • get set remove方法都会通过exoungStaleEntry方法回收k = null的键, 从而防止value内存泄漏

标签:map,ThreadLocalMap,value,ThreadLocal,线程,null
From: https://www.cnblogs.com/liyong888/p/17298466.html

相关文章

  • c#: AsyncLocal的使用,对比ThreadLocal
    一、先说ThreadLocal在以前写代码的时候,还没有异步的概念,那个时候我们处理HTTP请求就只用一个线程就搞定了,有的时候我们想在代码中共享一个对象,我们希望将这个对象绑定到线程上。如下:classProgram{privatestaticThreadLocal<WebContext>threadLocal=newThreadLoca......
  • ThreadLocal缺点及解决方案
    简单的一句话总结是每个Thread上都有一个threadLocals属性,它是一个ThreadLocalMap,里面存放着一个Entry数组,key是ThreadLocal类型的弱引用,value是对用的值。所有的操作都是基于这个ThreadLocalMap操作的。但是它有一个局限性,就是不能在父子线程之间传递。即在子线程中无法访问在父......
  • 线程间数据传递之ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal
    前言在JAVA中线程之间传输数据的方式有多种,而本文旨在探讨ThreadLocal及其衍生类的使用场景。使用场景业务系统的参数传递:在我们的业务系统中可能会用到许多公共参数,可能是用户的token信息,在我们链路中可能某一个方法需要用到它,那么我们又不想一层层的传递它。分布式系统要打......
  • FastThreadLocal源码解析
    Netty为什么要用自己的FastThreadLocal?threadLocalHash冲突,检索时间长。Netty自己定义的fastThreadLocal用的是数组,直接数组下标检索快。下面以ftl作为FastThreadLocal的简称例子ftl只有在FastThreadLocalThread线程中运行才生效,不然会走SlowGet模式(jdkthreadLocal方式)publiccl......
  • 【线程池】使用ThreadLocal请务必remove
    背景:在一次扫描中被提示:Field[SESSION_CONTEXT]oftypeThreadLocalmustcallremove()methodatleastonetimes.(line34)嗯?啥子情况?搜索了一下,发现:ThreadLo......
  • Java ThreadLocal
    ThreadLocal的功能在Java多线程并发环境中非常实用,其作用是提供线程本地变量,例如用户ID、会话ID等与当前线程密切关联的信息。那么它在实际业务场景中可以怎么使用呢?让我们......
  • ThreadLocal工作原理
     1.概述  ThreadLocal为我们解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。  ThreadLocal很容易让人望文生义......
  • 如何避免忘记清理 ThreadLocal ?
    一、背景ThreadLocal可以解决“线程安全问题”。也可以作为上下文暂存数据以备后续步骤获取。但是ThreadLocal用不好的确容易产生故障,因而有些团队不允许使用ThreadLoca......
  • PageHelper 使用 ThreadLocal 的线程复用问题,你用对了吗?(转载)
    前言PageHelper是较为常用的分页插件,通过实现Mybatis的Interceptor接口完成对querysql的动态分页,其中分页参数由ThreadLocal进行保存。简单的分页执行过程:......
  • 【InheritableThreadLocal】InheritableThreadLocal的实现机制和原理
    1 前言上节我们看了下ThreadLocal的实现原理,这节我们来看下 InheritableThreadLocal是用来干什么的呢?我们首先看个简单的现象:那我们把 ThreadLocal 换成 Inh......