笔者日常: 需要学点前端的东西!从今天开始,以后发博客按照二后一前的频率进行发表!!!
声明: 本文主要演示如何观察及证明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