温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

ThreadLocal的基本定义是什么

发布时间:2021-12-17 14:30:06 来源:亿速云 阅读:143 作者:柒染 栏目:大数据

这期内容当中小编将会给大家带来有关ThreadLocal的基本定义是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

基本定义及解读

官方释义: http://docs.oracle.com/javase/8/docs/api/
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

  • 首先,每个线程都有变量的一个本地独立副本,保证线程之间的数据不会互相影响

  • 可以通过重写initialValue()方法实现ThreadLocal的默认初始值

    protected static final ThreadLocal<String> TL_EXAMPLE = 
                new ThreadLocal<String>() {  @Override
      protected String initialValue() {      return "default";
      }
    };
  • 为什么说明中建议定义为静态static方法呢?不了解ThreadLocal原理的同学可能就糊涂了,既然是需要满足多线程并发的,怎么会定义为一个静态的类成员变量呢?
    只要大家看一下ThreadLocal的源码就了解了,它有个静态内部类叫ThreadLocalMap<ThreadLocal, Object>, 此Map在Thread类中被定义为了一个类成员变量,即每个Thread线程中都有一个独立ThreadLocalMap副本,它的值只能被当前线程读取和修改
    想像一下某个类中定义了多个ThreadLocal<?>变量,在当前线程中通过ThreadLocalMap.get(ThreadLocal)获取到相应的变量副本。
    所以ThreadLocal变量本身不是副本,你可以把他当成一个代理,而ThreadLocalMap中存放了线程内的一个一个线程副本,ThreadLocal只是ThreadLocalMap内弱引用的Key(在ThreadLocal对象失效时可以及时的清理ThreadLocalMap)。
    这也回答了为什么ThreadLocal可以定义为static, 它只是Map中的Key而已,不同线程的Map副本获取同一个Key的值完全不会冲突。

  类ThreadLocal:  
  public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);        if (e != null)            return (T)e.value;
    }    return setInitialValue();
  }  /**
  * ThreadLocalMap is a customized hash map suitable only for
  * maintaining thread local values. No operations are exported
  * outside of the ThreadLocal class. The class is package private to
  * allow declaration of fields in class Thread.  To help deal with
  * very large and long-lived usages, the hash table entries use
  * WeakReferences for keys. However, since reference queues are not
  * used, stale entries are guaranteed to be removed only when
  * the table starts running out of space.
  */
  static class ThreadLocalMap {...}

  类Thread:  /* ThreadLocal values pertaining to this thread. This map is maintained
   * by the ThreadLocal class. */
   ThreadLocal.ThreadLocalMap threadLocals = null;

Thread1 : 
       ThreadLocalMap1 : 
             <ThreadLocal1, String>
             <ThreadLocal2, Integer>Thread2 :
       ThreadLocalMap2 :             <ThreadLocal1, String>
             <ThreadLocal2, Integer>
  • 继续延伸出一个问题: ThreadLocal类本身是线程安全的么?
    通过源码看到不管是get,set还是createMap都没有做任何的同步或者并发锁。答案是安全的,因为实现都是基于当前线程的。

线程池与ThreadLocal变量的初始化

在线程池复用的情况下,若ThreaLocal数据没有被清理掉,会被后面的请求复用然后拿到被你修改过的值!
之前在实现日志上下文LogContext的时候碰到了类似问题:

  1. 请求A进入Controller中, 开启线程A,LogContext中记录了大量的ThreadLocal中间变量值,在请求响应结束后,请求线程A回归线程池;

  2. 请求B进入Controller中,复用线程A,LogContext会在之前变量值的基础上继续添加信息,这样的日志信息成了叠加的了。

不管是基于自己实现的线程池,还是应用服务器(如Tomcat)的线程池,都需要小心这一点!
标准或者规范做法是在线程变量使用完毕之后,或者finally代码块中调用 threadLocalVariable.remove() 移除,以防被其他线程复用。

上述就是小编为大家分享的ThreadLocal的基本定义是什么了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI