ThreadLocal

ThreadLocal 的作用主要是做数据隔离,填充的数据只属于当前线程,变量的数据对别的线程而言是隔离的,在多线程环境下,可以防止自己的变量被其它线程篡改。

Spring 采用 ThreadLocal 的方式,来保证单个线程的数据库操作使用的是同一个数据库连接。

底层原理:

set 的源码:

public void set(T value) {
    Thread t = Thread.currentThread(); // 获取当前线程
    ThreadLocalMap map = getMap(t);  // 获取 ThreadLocalMap对象
    if (map != null) // 校验对象是否为空
        map.set(this, value);  // 不为空set
    else
        createMap(t, value);  // 为空创建一个map对象
}    

ThreadLocalMap 是从当前线程的变量 threadLocals 中获取的。

 ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
class Thread implements Runnable {
	ThreadLocal.ThreadLocalMap threadLocals = null;
	ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}

每个线程 Thread 都维护了自己的 threadLocals 变量,所以每个线程创建 ThreadLocal 的时候,实际上数据是存在自己线程 Thread 的 threadLocals 变量里面的,别人没办法拿到,所以实现了隔离。

ThreadLocalMap 的底层结构

ThreadLocalMap 源码:

static class ThreadLocalMap {

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

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

ThreadLocalMap 的结构是数组加 Entry:
在这里插入图片描述

为什么用数组?

我们开发过程中一个线程可以有多个 ThreadLocal 来存放不同类型的对象,但是他们都放到 threadLocals 中,所以要用数组来存。

怎样解决 hash 冲突

ThreadLocalMap 在存储的时候会给每一个 ThreadLocal 对象一个 threadLocalHashCode。在插入过程中,根据 ThreadLocal 对象的 hash 值,定位到数组中的 i,int i = Key.threadLocalHashCode & (len - 1);

然后会判断一下:如果当前位置是空,就初始化一个 Entry 对象放在位置 i 上;如果位置 i 不为空,这个 Entry 对象的 Key 正好是即将设置的 Key,那么就刷新 Entry 中的 value;如果位置 i 不为空,Key 也不想等,那就找下一个位置,直到为空为止。

在 get 的时候,也会根据 ThreadLocal 对象的 hash 值,定位到数组中的位置,然后判断该位置 Entry 对象的 Key 是否和 get 的 Key 一致,如果不一致,就判断下一个位置,set 和 get 冲突如果严重的话,效率还是很低的。

怎样共享线程的 ThreadLocal 数据

使用 InheritableThreadLocal 可以实现多个线程访问 ThreadLocal 的值。

内存泄漏

ThreadLocal 在保存的时候会把自己当作 Key 存在 ThreadLcoalMap 中,但是 Key 被设计成 WeakReference 弱引用了。

弱引用:只具有弱引用的对象拥有更短暂的生命周期,在垃圾回收线程扫描它所管辖的内存区域的过程中,一旦发现只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

那么就会造成一个问题:ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部关联的强引用,那么在虚拟机进行垃圾回收时,这个ThreadLocal会被回收,这样,ThreadLocalMap中就会出现key为null的Entry,这些key对应的value也就再无妨访问,但是value却存在一条从Current Thread过来的强引用链。因此只有当Current Thread销毁时,value才能得到释放。如果创建ThreadLocal 的线程一直持续运行,那么就会发生内存泄漏。

解决方法:在代码的最后使用 remove 把值清空就好了。

为什么 ThreadLocalMap 的 key 要设计成弱引用

key 不设置成弱引用的话,那么 map 里面的 key 就不会被回收,也会发生内存泄漏。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值