千家信息网

ThreadLocal的常用方法、使用场景及注意事项有哪些

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,本篇内容主要讲解"ThreadLocal的常用方法、使用场景及注意事项有哪些",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"ThreadLocal的常用方法
千家信息网最后更新 2026年11月01日ThreadLocal的常用方法、使用场景及注意事项有哪些

本篇内容主要讲解"ThreadLocal的常用方法、使用场景及注意事项有哪些",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"ThreadLocal的常用方法、使用场景及注意事项有哪些"吧!

目录
  • 1. ThreadLocal详解

  • 2. ThreadLocal的使用场景

  • 3.常用方法源码解析

    • 3.1 initialValue方法

    • 3.2 set(T value)方法

    • 3.3 get方法

    • 3.4 小结

    • 3.4 ThreadLocalMap数据结构

  • 4. ThreadLocal的副作用

    • 4.1 ThreadLocal引起脏数据

    • 4.2 ThreadLocal引起的内存泄漏

  • 5. ThreadLocal内存泄漏解决方案及remove方法源码解析

    1. ThreadLocal详解

    JDK1.2版本起,Java就提供了java.lang.ThreadLocal,ThreadLocal为每个使用线程都提供独立的变量副本,可以做到线程间的数据隔离,每个线程都可以访问各自内部的副本变量。

    线程上下文ThreadLocal又称为"线程保险箱",ThreadLocal能够将指定的变量和当前线程进行绑定,线程之间彼此隔离,持有不同的对象实例,从而避免了数据资源的竞争。

    2. ThreadLocal的使用场景

    • 在进行对象跨层传递的时候,可以考虑ThreadLocal,避免方法多次传递,打破层次间的约束。

    • 线程间数据隔离。

    • 进行事务操作,用于储存线程事务信息。

    注意:

    ThreadLocal并不是解决多线程下共享资源的一种技术,一般情况下,每一个线程的ThreadLocal存储的都是一个全新的对象(通过new关键字创建),如果多线程的ThreadLocal存储了一个对象引用,那么就会面临资源竞争,数据不一致等并发问题。

    3.常用方法源码解析

    3.1 initialValue方法

     protected T initialValue() {        return null; }

    此方法为ThreadLocal保存的数据类型指定的一个初始化值,在ThreadLocal中默认返回null。但可以重写initialValue()方法进行数据初始化。

    如果使用的是Java8提供的Supplier函数接口更加简化:

    // withInitial()实际是创建了一个ThreadLocal的子类SuppliedThreadLocal,重写initialValue()ThreadLocal threadLocal = ThreadLocal.withInitial(Object::new);

    3.2 set(T value)方法

    主要存储指定数据。

    public void set(T value) {    // 获取当前线程Thread.currentThread()     Thread t = Thread.currentThread();    // 根据当前线程获取与之关联的ThreadLocalMap数据结构    ThreadLocalMap map = getMap(t);    if (map != null)        // 核心方法。set 遍历整个Entry的过程,后面有详解        map.set(this, value);    else {        // 调用createMap(),创建ThreadLocalMap,key为当前ThreadLocal实例,存入数据为当前value。        // ThreadLocal会创建一个默认长度为16Entry节点,并将k-v放入i位置(i位置计算方式和hashmap相似,        // 当前线程的hashCode&(entry默认长度-1)),并设置阈值(默认为0)为Entry默认长度的2/3。        createMap(t, value);    }}// set 遍历整个Entry的过程private void set(ThreadLocal key, Object value) {    // 获取所有的Entry    Entry[] tab = table;    int len = tab.length;    // 根据ThreadLocal对象,计算角标位置    int i = key.threadLocalHashCode & (len-1); // 循环查找    for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {        ThreadLocal k = e.get();  // 找到相同的就直接覆盖,直接返回。        if (k == key) {            e.value = value;            return;        }  // 如果ThreadLocal为null,直接驱出并使用新数据(Value)占居原来位置,  // 这个过程主要是防止内存泄漏。        if (k == null) {            // 驱除ThreadLocal为null的Entry,并放入Value,这也是内存泄漏的重点地区            replaceStaleEntry(key, value, i);            return;        }    } // entry都为null,创建新的entry,已ThreadLocal为key,将存放数据为Value。    tab[i] = new Entry(key, value);    int sz = ++size;    // ThreadLoaclMapde的当前数据元素的个数和阈值比较,再次进行key为null的清理工作。    if (!cleanSomeSlots(i, sz) && sz >= threshold)        // 整理Entry,当Entry中的ThreadLocal对象为null时,通过重新计算角标位来清理        // 以前ThreadLocal。如果Entry数量大于3/4容量进行扩容        rehash();}

    3.3 get方法

    get()用于返回当前线程ThreadLocal中数据备份,当前线程的数据都存在一个ThreadLocalMap的数据结构中。

    public T get() {    Thread t = Thread.currentThread();    // 获得ThreadLocalMap对象map,ThreadLocalMap是和当前Thread关联的,    ThreadLocalMap map = getMap(t);    if (map != null) {        // 存入ThreadLocal中的数据实际上是存储在ThreadLocalMap的Entry中。        // 而此Entry是放在一个Entry数组里面的。        // 获取当前ThreadLocal对应的entry        ThreadLocalMap.Entry e = map.getEntry(this);        if (e != null) {            // 直接返回当前数据             T result = (T)e.value;            return result;        }    }    // ThreadLocalMap未初始化,首先初始化    return setInitialValue();}// ThreadLocal的setInitialValue方法源码private T setInitialValue() {    // 为ThreadLocalMap指定Value的初始化值    T value = initialValue();    Thread t = Thread.currentThread();    // 根据本地线程Thread获取ThreadLocalMap,一下方法与Set方法相同。    ThreadLocalMap map = getMap(t);    if (map != null)        // 如果map存在,直接调用set()方法进行赋值。        map.set(this, value);    else        // map==null;创建ThreadLocalMap对象,并将Thread和value关联起来        createMap(t, value);    return value;}

    3.4 小结

    • initialValue():初始化ThreadLocal中的value属性值。

    • set():获取当前线程,根据当前线程从ThreadLocals中获取ThreadLocalMap数据结构,

      • 如果ThreadLocalmap的数据结构没创建,则创建ThreadLocalMap,key为当前ThreadLocal实例,存入数据为当前value。ThreadLocal会创建一个默认长度为16Entry节点,并将k-v放入i位置(i位置计算方式和hashmap相似,当前线程的hashCode&(entry默认长度-1)),并设置阈值(默认为0)为Entry默认长度的2/3。

      • 如果ThreadLocalMap存在。就会遍历整个Map中的Entry节点,如果entry中的key和本线程ThreadLocal相同,将数据(value)直接覆盖,并返回。如果ThreadLoca为null,驱除ThreadLocal为null的Entry,并放入Value,这也是内存泄漏的重点地区。

    • get()

    • get()方法比较简单。就是根据Thread获取ThreadLocalMap。通过ThreadLocal来获得数据value。注意的是:如果ThreadLocalMap没有创建,直接进入创建过程。初始化ThreadLocalMap。并直接调用和set方法一样的方法。

    3.4 ThreadLocalMap数据结构

    set()还是get()方法都是避免不了和ThreadLocalMap和Entry打交道。ThreadLocalMap是一个类似于HashMap的一个数据结构(没有链表),仅仅用于存放线程存放在ThreadLocal中的数据备份,ThreadLocalMap的所有方法对外部都是不可见的。

    ThreadLocalMap中用于存储数据的Entry,它是一个WeakReference类型的子类,之所以设计成WeakReference是为了能够是JVM发生gc,能够自动回收,防止内存溢出现象。

    4. ThreadLocal的副作用

    4.1 ThreadLocal引起脏数据

    线程复用会产生脏数据。

    由于结程池会重用 Thread 对象 ,那么与 Thread 绑定的类的静态属性 ThreadLocal 变量也会被重用。如果在实现的线程 run()方法体中不显式地调用 remove() 清理与线程相关的ThreadLocal 信息,那么如果下一个线程不调用set()设置初始值,就可能 get()到重用的线程信息,包括 ThreadLocal 所关联的线程对象的 value 值。

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

    4.2 ThreadLocal引起的内存泄漏

    在上面提到ThreadLocalMap中存放的Entry是WeakReference的子类。所以在JVM触发GC(young gc,Full GC)时,都会导致Entry的回收

    在get数据的时候,增加检查,清除已经被回收器回收的Entry(WeakReference可以自动回收)

    private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {    ThreadLocal k = e.get();  ...    if (k == null)        // 清除 key 是 null 的Entry        expungeStaleEntry(i);  ... return null;}private boolean cleanSomeSlots(int i, int n) {    boolean removed = false;    Entry[] tab = table;    int len = tab.length;    do {        i = nextIndex(i, len);        Entry e = tab[i];        if (e != null && e.get() == null) {            n = len;            removed = true;            // 清除key==null 的Entry            i = expungeStaleEntry(i);        }    } while ( (n >>>= 1) != 0);    return removed;}

    set数据时增加检查,删除已经被垃圾回收器清理的Entry,并将其移除

    private boolean cleanSomeSlots(int i, int n) {    boolean removed = false;    Entry[] tab = table;    int len = tab.length;    do {        i = nextIndex(i, len);        Entry e = tab[i];        if (e != null && e.get() == null) {            n = len;            removed = true;            // 清除key==null 的Entry            i = expungeStaleEntry(i);        }    } while ( (n >>>= 1) != 0);    return removed;}

    基于上面三点:ThreadLocal在一定程度上保证不会发生内存泄漏。但是Thread类中有ThreadlocalMap的引用,导致对象的可达性,故不能回收。

    ThreadLocal被置为null清除了。但是通过ThreadLocalMap还是被Thread类引用。导致该数据是可达的。所以内存得不到释放,除非当前线程结束,Thread引用就会被垃圾回收器回收。如图所示

    5

    5. ThreadLocal内存泄漏解决方案及remove方法源码解析

    解决ThreadLocal内存泄漏的常用方法是:在使用完ThreadLocal之后,及时remove掉。

    public void remove() {    // 根据当前线程,获取ThreadLocalMap    ThreadLocalMap m = getMap(Thread.currentThread());    if (m != null)        // map不为null,执行remove操作        m.remove(this);}// ThreadLocal 的remove()private void remove(ThreadLocal key) {    // 获取存放key-value的数组。    Entry[] tab = table;    int len = tab.length;    // 根据ThreadLocal的HashCode确定唯一的角标    int i = key.threadLocalHashCode & (len-1);    for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {        if (e.get() == key) {            // 如果和本ThreadLocal相同。将引用置null。            e.clear();            // 实行Enty和Entry.value置null。源码中 tab[staleSlot].value = null; tab[staleSlot] = null;            expungeStaleEntry(i);            return;        }    }}

    到此,相信大家对"ThreadLocal的常用方法、使用场景及注意事项有哪些"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

    数据 线程 方法 内存 对象 数据结构 结构 常用 位置 源码 长度 场景 存储 相同 变量 过程 并将 关联 事项 注意事项 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 云南咖啡 南京欣网互联网络科技 互联网科技园 深圳市人人互联网科技有限公司 深圳卡尔迅互联网科技有限公司怎么样 团飞科技与伯堂互联网服务平台 互联网大会黑科技排名 互联网科技有哪些公司 彤创互联网科技有限公司 注册互联网科技公司经营范围大全 如何创新互联网科技 洛阳共同富裕互联网科技有限公司 华夏恒生科技与互联网科技 四川慕妮娅互联网科技有限公司 互联网金融科技元年 伍佰万互联网科技怎么赚钱 互联网科技平台产品运营 万物互联网智能科技 湖北辰鑫科技互联网 科技互联网企业在行动 科技企业和互联网企业 奇酷互联网络科技 天扬科技互联网广告 互联网汽车科技小镇 宁夏众和广鑫互联网科技有限公司 昊天互联网科技有限公司 厦门千工互联网科技有限公司 新兴科技互联网创新大赛颁奖典礼 长沙互联网科技 深圳互联网科技的发展
    0