• ThreadLocal源码解析以及常见面试题


    ThreadLocal源码解析

    ThreadLocalMap

    属于Thread中的一个静态内部类

    
    public class Thread implements Runnable {{
    	ThreadLocal.ThreadLocalMap threadLocals = null;
    
    }
    
    
    public class ThreadLocal<T>{
    
    	//这里是真正存储数据的地方
    	ThreadLocal.ThreadLocalMap threadLocals = null;
    //这是一个静态内部类    
    static class ThreadLocalMap {
    
    	static class Entry extends WeakReference<ThreadLocal<?>> {
    	}
    	}
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    面试题

    一、ThreadLocal存储的key,value分别是什么?存储在什么地方?

    1. ThreadLocal存储的key是new ThreadLocal对象本身,所以一个ThreadLocal对象只有一个Value,多次set会覆盖。
    2. 存储的位置在Thread的成员变量中,如下所示:
      ThreadLocal.ThreadLocalMap threadLocals = null;
      一个Thread只有一个ThreadLocalMap,ThreadLocalMap中有一个entery数组,如下所示:
      private Entry[] table;

    Entry的所有代码如下,我们的Value就存储在Entry的value中。

    static class Entry extends WeakReference<ThreadLocal<?>> {
                /** The value associated with this ThreadLocal. */
                Object value;
    
                Entry(ThreadLocal<?> k, Object v) {
                    super(k);
                    value = v;
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    二、一个ThreadLocal只有一个Value,为什么要使用Entry数组存储呢?其他的空间是不是浪费?

    因为一个Thread只有一个ThreadLocalMap,而我们却可以在一个Thread线程中创建多个ThreadLocal对象?如下所示:

    public class Test{
    	private ThreadLocal td1 = new ThreadLocal();
    	private ThreadLocal td2 = new ThreadLocal();
        private ThreadLocal td3 = new ThreadLocal();
        // 我们可以创建多个ThreadLocal
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    当多个ThreadLocal对象set值时,我们会根据ThreadLocal对象的hashcode计算出其所在的Entry数组的下标位置,计算方式如下(是不是跟HashMap计算数组下标类似)
    int i = key.threadLocalHashCode & (len-1);(相当于取余数)

    总结:所以使用Entry数组存储ThreadLocal的value的是为了一个Thread中创建多个ThreadLoca对象。

    三、Entry为什么要继承WeakReference弱引用呢?

    1.观察如下代码,当method()调用完成后程序继续执行,此时ThreadLocal的对象引用会被回收掉,如果Entry是强引用的情况下就会发送内存泄漏。

    public void test(){
        method();
        //程序继续执行
        //more code ,继续处理业务逻辑
    }
    
    public void method(){
    	ThreadLocal td = new ThreadLocal();
    	td.set("1GB");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    原因分析:
    当继续处理业务逻辑时,method方法已经执行完成了,ThreadLocal的引用(td)已经断开,按理应该回收堆中的ThreadLocal实例,可是却被key(红色)的所引用,无法回收到,由于主线程对象一直还在运行,所以ThreadLocalMap实例一直无法回收,导致Entry数组中的key和value也就无法回收调,此时就发送了内存泄漏。

    解决方案;

    如果此时Entry替换成弱引用(黄色虚线),则ThreadLocal只剩下一个弱引用,会立即被回收,此时Entry对象会被回收,那么value自然也会被回收调。

    关于弱引用的规范如下:

    垃圾回收器一旦发现只具有弱引用的对象,不管当前内存空间是否足够,都会回收它的内存

  • 相关阅读:
    Qt 打印调试信息-怎样获取QTableWidget的行数和列数-读取QTableWidget表格中的数据
    Elasticsearch
    了解过的国内名师
    反射(类加载、加载流程、加载的五个阶段、获取类结构信息、反射暴破创建实例、操作属性、操作方法)
    数据结构第一课-----------数据结构的介绍
    我的大一.
    阿里云 短信服务——短信发送频率限制
    JavaScript大作业(餐厅美食网站设计与实现)
    k8s之配置资源管理
    乘方【NOIP 2022 普及组】
  • 原文地址:https://blog.csdn.net/weixin_48470176/article/details/126199305