• 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自然也会被回收调。

    关于弱引用的规范如下:

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

  • 相关阅读:
    Oracle/PLSQL: Lower Function
    《进阶篇第9章》学习vuex知识点后练习:把求和案例改成vuex模块化编码
    从kafka如何保证数据一致性看通常数据一致性设计
    关于安卓artifactory本地仓库搭建
    Redis 数据类型详细解析
    Linux篇17多线程第一部分
    如何使用滚动字幕为视频增添动态效果?
    scrapy框架——架构介绍、安装、项目创建、目录介绍、使用、持久化方案、集成selenium、去重规则源码分析、布隆过滤器使用、redis实现分布式爬虫
    防爆对讲机在消防救援工作中的重要性
    java-php-net-python-简历网站计算机毕业设计程序
  • 原文地址:https://blog.csdn.net/weixin_48470176/article/details/126199305