ThreadLocal数据观察及原理验证

笔者日常 需要学点前端的东西!从今天开始,以后发博客按照二后一前的频率进行发表!!!


声明 本文主要演示如何观察及证明ThreadLocal,ThreadLocal原理会几句话带过。


ThreadLocal原理(简述)

        Thread类有一个threadLocals字段,该字段类型为ThreadLocal.ThreadLocalMap。简单地说,threadLocals以ThreadLocal<T>实例对象的弱引用为key,以T为value,进行数据存储。

  • 当在线程A里面调用ThreadLocal实例的set(T value)方法时,就(可以这么理解)相当于一个map对象(一个线程对应一个这样的map对象)以这个ThreadLocal对象的弱引用为key,以set的具体值为值,将其进行存储。
  • 当在线程A里面调用ThreadLocal实例的get()方法时,就(可以这么理解)相当于一个map对象(一个线程对应一个这样的map对象)以这个ThreadLocal对象的弱引用为key,进行取值。

注:ThreadLocal的核心在于ThreadLocalMap,这里本人不再进行相关知识介绍,感兴趣的,可自行阅读源
       码或查阅相关资料。这篇博客讲得比较细,可参考阅读。


ThreadLocal数据观察及验证

声明: ThreadLocal核心为ThreadLocal$ThreadLocalMap,而ThreadLocalMap$ThreadLocalMap的
           核心为table(该字段类型为ThreadLocal$ThreadLocalMap$Entry数组),下面主要观察各个线程
           的table数据信息。

编写输出ThreadLocal$ThreadLocalMap$Entry信息的工具类

/**
 * 获得指定Thread的threadLocals属性的table值
 * 注:table的数据类型为java.lang.ThreadLocal$ThreadLocalMap$Entry[]
 *
 * @param targetThread
 *            要获取的线程
 * @return  该线程对应的的threadLocals属性的table值 的格式化输出
 * @date 2019/9/27 10:50
 */
private static String getThreadLocalMapTableInfo(Thread targetThread) {
    // 先获得class对象
    Class threadClazz = targetThread.getClass();
    Object threadLocalsValue;
    Object tableValue;
    try {
        /// -> 从Thread中获取到 当前线程的threadLocals(注:该字段类型为ThreadLocalMap)的值
        // 获得targetThread对应类的threadLocals属性,并设置属性可访问
        Field threadLocalsField = threadClazz.getDeclaredField("threadLocals");
        threadLocalsField.setAccessible(true);
        // 从targetThread对象中,获取threadLocals属性的值
        // 这里即:获取到Thread类的threadLocals属性值
        threadLocalsValue = threadLocalsField.get(targetThread);

        /// -> 从ThreadLocalMap中获取到tables数组(注:该字段类型为Entry[])的值
        Class threadLocalMapClazz = threadLocalsValue.getClass();
        // 获得threadLocalMapClazz对应类的table属性,并设置属性可访问
        Field tableField = threadLocalMapClazz.getDeclaredField("table");
        tableField.setAccessible(true);
        // 从threadLocalsValue对象中,获取table属性的值
        // 这里即:获取到ThreadLocal$ThreadLocalMap类的table属性值
        tableValue = tableField.get(threadLocalsValue);
    } catch (NoSuchFieldException | IllegalAccessException e) {
       throw new RuntimeException(e);
    }
    return systemOutEntry(targetThread, (Object[])tableValue);
}

/**
 * 优雅输出java.lang.ThreadLocal$ThreadLocalMap$Entry[]实例的信息
 */
private static String systemOutEntry(Thread targetThread, Object[] array) {
    StringBuilder sb = new StringBuilder(64);
    try {
        /// -> 预处理java.lang.ThreadLocal$ThreadLocalMap$Entry的key
        Class referenceClass = Class.forName("java.lang.ref.Reference");
        Field keyField = referenceClass.getDeclaredField("referent");
        keyField.setAccessible(true);

        /// -> 预处理java.lang.ThreadLocal$ThreadLocalMap$Entry的value
        Class entryClass = array.getClass().getComponentType();
        Field valueField = entryClass.getDeclaredField("value");
        valueField.setAccessible(true);

        String key;
        String value;
        sb.append("线程");
        sb.append(targetThread.getName());
        sb.append("的threadLocals属性值(即:ThreadLocal.ThreadLocalMap对象)的table(数组对象)的信息为:");
        sb.append("\n");
        for (int i = 0; i < array.length; i++) {
            value = array[i] == null ? null : String.valueOf(valueField.get(array[i]));
            key = array[i] == null ? null : String.valueOf(keyField.get(array[i]));
            sb.append("\t").append("index => ").append(i).append(", ");
            sb.append("\t").append("{").append(key).append(": ").append(value).append("}");
            sb.append("\n");
        }
    } catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) {
        throw new RuntimeException(e);
    }
    return sb.toString();
}

测试及说明

  • 运行以下测试方法:
    /** 一个ThreadLocal */
    private static final ThreadLocal<List<Integer>> TD_LIST = new ThreadLocal<>();
    
    /** 另一个ThreadLocal */
    private static final ThreadLocal<String> TD_STRING = new ThreadLocal<>();
    
    /**
     * 主方法
     *
     * @date 2019/9/26 15:07
     */
    @SuppressWarnings("all")
    public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException {
        System.out.println("对象的TD_LIST签名为: " + TD_LIST);
        System.out.println("对象的TD_STRING签名为: " + TD_STRING);
    
        // 线程一
        Thread threadOne = new Thread(() -> {
            TD_STRING.set(new String("JustryDeng"));
            TD_LIST.set(Arrays.asList(1, 3, 5, 7, 9));
            // sleep几秒,保证getThreadLocalMapTableInfo(xxx)执行时,线程尚未结束
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        threadOne.start();
    
        // 线程二
        Thread threadTwo = new Thread(() -> {
            TD_STRING.set(new String("邓沙利文"));
            TD_LIST.set(Arrays.asList(2, 4, 6, 8, 10));
            // sleep几秒,保证getThreadLocalMapTableInfo(xxx)执行时,线程尚未结束
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        threadTwo.start();
        System.out.println(getThreadLocalMapTableInfo(threadOne));
        System.out.println(getThreadLocalMapTableInfo(threadTwo));
    }
    
    • 控制台输出:
      在这里插入图片描述
             上图中只能证明key是ThreadLocal的引用,但不能证明是弱引用,这个只需要看一下源码(如下图)就可知其实际上是弱引用了:
      在这里插入图片描述

由此可见,本文开头的原理介绍得到了验证!


^_^ 如有不当之处,欢迎指正

^_^ 参考链接
        https://www.cnblogs.com/micrari/p/6790229.html

^_^ 本文已经被收录进《程序员成长笔记》 ,笔者JustryDeng

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值