• java ThreadLocal


    1. private ThreadLocal threadLocal = new ThreadLocal();
    2. threadLocal.set(0);
    3. (int) threadLocal.get();

    上面三行代码分别是定义、赋值和取值。

    介绍:

    我们只需要实例化对象一次,并且也不需要知道它是被哪个线程实例化。虽然所有的线程都能访问到这个ThreadLocal实例,但是每个线程却只能访问到自己通过调用ThreadLocal的set()方法设置的值。即使是两个不同的线程在同一个ThreadLocal对象上设置了不同的值,他们仍然无法访问到对方的值。

    需要框架源码的朋友可以看我个人简介联系我,推荐分布式架构源码。 

    各个线程赋值读取互补干扰的原理:

    源码中有一个ThreadLoalMap类型的东西,理解成map类型即可。

    详解一下取值过程,调用ThreadLocal的get方法时,会先调用getMap(t)获取到ThreadLoalMap的集合,其中参数t为当前线程(Thread.currentThread()),getMap方法表示每个线程对象中都维护有这么个ThreadLoalMap对象集合。

    1. public T get() {
    2. Thread t = Thread.currentThread();
    3. ThreadLocalMap map = getMap(t);
    4. if (map != null) {
    5. ThreadLocalMap.Entry e = map.getEntry(this);
    6. if (e != null) {
    7. @SuppressWarnings("unchecked")
    8. T result = (T)e.value;
    9. return result;
    10. }
    11. }
    12. return setInitialValue();
    13. }
    1. ThreadLocalMap getMap(Thread t) {
    2. return t.threadLocals;
    3. }

    然后,调用该集合的getEntry方法,参数就是ThreadLocal对象本身,ThreadLocal的hash值和table.length构成了Entry的键。

    1. private Entry getEntry(ThreadLocal key) {
    2. int i = key.threadLocalHashCode & (table.length - 1);
    3. Entry e = table[i];
    4. if (e != null && e.get() == key)
    5. return e;
    6. else
    7. return getEntryAfterMiss(key, i, e);
    8. }

    总结下:

    每个线程都维护有一个ThreadLocalMap对象集合,他的键就是ThreadLocal对象的hash(相当于这么个东西),这样A、B两个线程就会各自维护一个ThreadLocalMap对象集合,共用一个ThreadLocal对象的hash值当作其中的键,就相当于A线程的ThreadLocalMap有共用的hash键,B线程的ThreadLocalMap也有一个共用的hash键。这样就不会冲突了,有点绕,多查资料多理解。

    InheritableThreadLocal其中还有这么个东西,InheritableThreadLocal类是ThreadLocal类的子类。ThreadLocal中每个线程拥有它自己的值,与ThreadLocal不同的是,InheritableThreadLocal允许一个线程以及该线程创建的所有子线程都可以访问它保存的值。相当于一个类定义成protected。

    1. 每个Thread实例内部,有二个ThreadLocalMap的K-V容器实例(分别对应threadLocals及inheritableThreadLocals), 容器的元素数量,即为Thread实例里的ThreadLocal实例个数
    2. ThreadLocalMap里的每个Entry的Key与ThreadLocal实例的HashCode相关(这样,多个ThreadLocal实例就不会搞混)
    3. 每个ThreadLocal实例使用set赋值时,实际上是在ThreadLocalMap容器里,添加(或更新)一条Entry信息
    4. 每个ThreadLocal实例使用get取值时,从ThreadLocalMap里根据key取出value 。

    关于内存泄漏问题:

    通过之前的分析已经知道,当使用ThreadLocal保存一个value时,会在ThreadLocalMap中的数组插入一个Entry对象,按理说key-value都应该以强引用保存在Entry对象中,但在ThreadLocalMap的实现中,key被保存到了WeakReference对象中,源码中是继承WeakReference对象了。

    static class Entry extends WeakReference>

    ThreadLocal在ThreadLocalMap中是以一个弱引用身份被Entry中的Key引用的,因此如果ThreadLocal没有外部强引用来引用它,那么ThreadLocal会在下次JVM垃圾收集时被回收。这个时候就会出现Entry中Key已经被回收,出现一个null Key的情况,外部读取ThreadLocalMap中的元素是无法通过null Key来找到Value的。因此如果当前线程的生命周期很长,一直存在,那么其内部的ThreadLocalMap对象也一直生存下来,这些null key就存在一条强引用链的关系一直存在:Thread --> ThreadLocalMap-->Entry-->Value,这条强引用链会导致Entry不会回收,Value也不会回收,但Entry中的Key却已经被回收的情况,造成内存泄漏。

    但是JVM团队已经考虑到这样的情况,并做了一些措施来保证ThreadLocal尽量不会内存泄漏:在ThreadLocal的get()、set()、remove()方法调用的时候会清除掉线程ThreadLocalMap中所有Entry中Key为null的Value,并将整个Entry设置为null,利于下次内存回收。

  • 相关阅读:
    canvas实现百度AI图片多主体识别效果
    处理验证码和登录页面
    Zookeeper系列——4Zookeeper的Watcher机制原理分析
    sed去除文件中的引号
    Redis Hotkey?3招定位+5招解决
    手机上比较好用的笔记软件使用哪一款?
    题目地址(04.06. 后继者)
    宝塔Python3.7安装模块报错ModuleNotFoundError: No module named ‘Crypto‘解决办法
    算法精选(一)
    子类到底能不能继承父类的私有属性?
  • 原文地址:https://blog.csdn.net/m0_61571842/article/details/125999149