ThreadLocal 的应用总结

ThreadLocal 是一个线程的局部变量,只有当前线程可以访问,是线程安全的。在使用时,需要为每一个线程分配不同的对象时,需要在应用层面保证。即 ThreadLocal 只是起到了简单的容器作用。如果在应用上每个线程分配了相同的对象实例,那么 ThreadLocal 也不能保证线程安全。这一点需要注意

比如 :SimpleDateFormat 对象不是线程安全的,在使用的时候,就需要使用 ThreadLocal 为每一个线程都产生一个 SimpleDateFormat 对象实例

ThreadLocal 实现原理

首先从 set() 方法开始说起

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

在 set 时,首先获得当前线程对象,然后通过 getMap() 拿到线程的 ThreadLocalMap,并将值设入 ThreadLoclMap 中。而 ThreadLocalMap 可以理解为一个 Map,但是它是定义在 Thread 内部的成员(ThreadLocalMap 本身是 ThreadLocal 的一个内部类),在 Thread 类的第 182 行中可以看到:

  ThreadLocal.ThreadLocalMap threadLocals = null;

而设置到 ThreadLocal 中的数据,也正是写入到了Thread类中 threadLocals 这个 Map。其中,key 为 ThreadLocal 当前对象,value 就是我们需要的值。而 threadLocals 本身就保存了当前自己所在线程的所有 “局部变量”,也就是一个 ThreadLocal 变量的集合。

在进行 get() 操作时,自然就是将这个 Map 中的数据拿出来:

public T get() {
        Thread t = Thread.currentThread();
        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();
    }

首先,get() 方法也是先取得当前线程的 ThreadLocalMap 对象。然后,通过将自己,即 ThreadLocal 本身作为 key 取得内部的实际数据。

在了解了 ThreadLocal 的内部实现后,我们自然会引出一个问题。那就是这些变量是维护在 Thread 类内部的(ThreadLocalMap 定义所在类),这也意味着只要线程不退出,对象的引用将一直存在。
当线程退出时,Thread 类会进行一些清理工作,其中就包括 ThreadLocalMap 。

private void exit() {
        if (group != null) {
            group.threadTerminated(this);
            group = null;
        }
        /* Aggressively null out all reference fields: see bug 4006245 */
        target = null;
        /* Speed the release of some of these resources */


        threadLocals = null;


        inheritableThreadLocals = null;
        inheritedAccessControlContext = null;
        blocker = null;
        uncaughtExceptionHandler = null;
    }

因此,如果我们使用线程池,那就意味着当前线程未必会退出(比如固定大小的线程池,线程总是存在)。如果这样,将一些大对象设置到 ThreadLocal 中(它实际保存在线程持有的 threadLocals 中),可能会使系统出现内存泄露的可能。
此时,如果你希望及时回收对象,最好使用 ThreadLcoal.remove() 方法将这个变量移除。就像我们习惯性地关闭数据库连接一样。

另外,如果对 ThreadLocal 的变量手动设置为 null,比如 tl = null ,那么这个 ThreadLocal 对应的所有线程的局部变量都有可能被回收。要了解这里的回收机制,我们需要更进一步了解 ThreadLocalMap 的实现。

ThreadLocalMap 的实现使用了弱引用。弱引用是比强引用弱得多的引用。Java 虚拟机在垃圾回收时,如果发现弱引用,就会立即回收。ThreadLocalMap 内部由一系列 Entry 构成,每一个 Entry 都是 WeakReference :

static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

这里的参数 k 就是 Map 的 key ,v 就是 Map 的 value。其中 k 也就是 ThreadLocal 的实例。作为弱引用使用( super(k)就是调用了 WeakReference 的构造函数)。因此,虽然这里使用 ThreadLocal 作为 Map 的 key,但是实际上,它并不真的持有 ThreadLocal 的引用。而当 ThreadLocal 外部强引用被回收时,ThreadLocalMap 中的 key 就会变成 null 。当系统进行 ThreadLocalMap 清理时,就会自然将这些垃圾数据回收。

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值