美文网首页
我赌你不懂系列:你没用过ThreadLocal

我赌你不懂系列:你没用过ThreadLocal

作者: 分布式与微服务 | 来源:发表于2022-07-04 09:10 被阅读0次
image.png

前言

先抛几个小问题吧。。。

  1. 如题,不会真有Java程序员没有用过ThreadLocal类吧?
  2. Java中的的引用类型有哪几种?
  3. ThreadLocal应用场景都有哪些?
  4. ThreadLocal会产生内存泄漏你了解吗? 啊? 啥是内存泄露?

前段时间工作中做了一个Spring的动态数据源切换的小东西,是通过aop根据不同包下的请求来实现动态切换,为了防止多个线程同时请求的时候导致数据连接错乱就用到了ThreadLocal。

这个怎么理解呢...比如线程a用的是a数据源,线程b用的b数据源,线程ab同时进入可能会导致a使用了b数据源。

ThreadLocal

如果你读过spring事务控制源码的话应该知道spring中的connection就有放在ThreadLocal中。

ThreadLocal一般称为线程本地变量, 也就是说一个ThreadLocal的变量只有当前线程可以访问。

每个线程都可以通过set()和get()来对这个局部变量进行操作,并不会和其他线程的局部变量发生冲突。

总结一句话:当前线程使用ThreadLocal进行set的值只能当前线程通过get()获取到,别的线程不行。

set

接下来看一张图:

image.png

结合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);
    }

在前面我们说调用ThreadLocal.set(Obj)能把Obj对象存储在当前线程空间内,通过代码可以看出其实不是将Obj放入ThreadLocal中,而是将Obj放在当前线程Thread当中的一个Map属性中,而这个map的key竟然就是调用set()的ThreadLocal对象。
那我们来看下这个Map长什么样。

public class Thread implements Runnable {
   ThreadLocal.ThreadLocalMap threadLocals = null;
}

欧吼,又发现这个Map竟然是ThreadLocal类里的静态内部类。

Map中的Entry应该没人看不懂吧.... 就是这个Map中有个属性Entry[] table,就是用来存我们的key value对。

public class ThreadLocal<T> {   
    static class ThreadLocalMap {
            static class Entry extends WeakReference<ThreadLocal<?>> {
                Object value;

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

注意! 这里的Entry竟然是WeakReference弱引用的子类!Entry的构造器中调用了WeakReference的构造器,导致每个Entry的key都是一个弱引用,Entry中的value依然是强引用。

这个弱引用引用的是ThreadLocal对象内存空间,而我们在新建ThreadLocal对象的时候一般是new出来的,

ThreadLocal<String> tl = new ThreadLocal<>();

也有个变量tl强引用着这个ThreadLocal对象。

内存泄露问题

内存泄露 Memory Leak:该回收的垃圾对象没有被回收,发生了内存泄露,垃圾对象越堆越多,可用内存越来越少,若可用内存无法存放新的垃圾对象,就会导致内存溢出。
内存溢出 Out Of Memory:当前创建的对象的大小大于可用的内存容量大小,发生内存溢出。
内存泄露会导致内存溢出。

当外部的强引用消失,如tl=null,那这个对象也就没啥意义了,现在只有我们的弱引用引用着这个对象内存空间了,它自然阻止不了垃圾回收掉这片空间。

而我们Entry的value还是强引用啊,我key都没了,你给我留着value有啥用啊?这就会导致内存泄露,若可用内存无法存放新的垃圾对象,又会导致内存溢出。

那怎么解决这个问题呢,我们只需要在使用完ThreadLocal后手动的调用ThreadLocal的remove方法就可以了。

get

get方法就是从先获取到当前Thread,然后拿到自身属性的map对象,根据ThreadLocal这个key去查看有没有对应Entry,有就获取value返回,没有则初始化一个value为null的Entry放入map中,返回null。

    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();
    }

应用场景

首先要明白使用ThreadLocal是以耗费内存为代价的。

  1. 在多层嵌套的方法中替代参数的显式传递

    也就是在上下文传递信息,线程内的所有方法都能获取到,避免一些参数传递。

  2. 多数据源动态切换

相关文章

  • 我赌你不懂系列:你没用过ThreadLocal

    前言 先抛几个小问题吧。。。 如题,不会真有Java程序员没有用过ThreadLocal类吧? Java中的的引用...

  • 《赌》

    我赌你最后娶的是我 我赌我赌赢 我赌 赌

  • 我赌你不懂系列:Java的引用类型都有哪几种

    引用类型 今天看代码看到有牵扯到弱引用的东西,就先稍微补一补Java的四种引用类型吧。 Java为引用类型专门定义...

  • Handler(三)--ThreadLocal

    系列目录: Handler机制原理 ThreadLocal介绍 ThreadLocal 是一个线程内部的数据存储类...

  • 满盘皆输

    我一直在赌 赌你爱不爱我 赌你有没有想我 赌你会不会回来找我 最后啊 我满盘皆输

  • 2019-11-25

    我不赌你喜欢我, 我赌自己不会后悔

  • 〖闞〗这一跪

    这一跪 有太多赌的成分 你赌他念你多年的辛劳 你赌他心里有一念慈悲 你赌他不在意你老无所用 你赌他眼里没有肉骨可兑...

  • 用我的心作高墙— 把你囚起 你可知这高墙 让你践踏得多么无力 赌,赌,赌 也许你认为是你的空气 纸醉金迷几乎输掉了...

  • 你没用过Mac 所以你不知道Mac 有多美

    你没用过Mac 所以你不知道Mac 有多美。 你没用过Mac ,所以你不知道Mac 上Xcode DIY是多好的视...

  • 2017-08-31

    你在和赌 我不会让你输

网友评论

      本文标题:我赌你不懂系列:你没用过ThreadLocal

      本文链接:https://www.haomeiwen.com/subject/mucdbrtx.html