前言
ThreadLocal :每个线程通过此对象都会返回各自的值,互不干扰,这是因为每个线程都存着自己的一份副本。需要注意的是线程结束后,它所保存的所有副本都将进行垃圾回收(除非存在对这些副本的其他引用)
ThreadLocal的get操作是这样执行的:ThreadLocalMap map = thread.threadLocals -> return map.getEntry(threadLocal)ThreadLocal的set操作是这样执行的:ThreadLocalMap map = thread.threadLocals -> map.set(threadLocal, value)
三者的关系是:
每个Thread对应的所有ThreadLocal副本都存放在ThreadLocalMap对象中,key是ThreadLocal,value是副本数据
ThreadLocalMap对象存放在Thread对象中
通过ThreadLocal获取副本数据时,实际是通过访问Thread来获取ThreadLocalMap,再通过ThreadLocalMap获取副本数据
示例代码如下:
importjava.lang.reflect.Field;
importjava.util.Arrays;
importjava.util.List;
importjava.util.concurrent.CountDownLatch;
importjava.util.stream.Collectors;
/**
* @author: lihui
* @date: 2020-06-01
*/
publicclassThreadLocalStudy {
privatestaticfinal ThreadLocal threadLocal1 =newThreadLocal<>();
privatestaticfinal ThreadLocal threadLocal2 =newThreadLocal<>();
privatestaticCountDownLatch countDownLatch1 =newCountDownLatch(2);
privatestaticCountDownLatch countDownLatch2 =newCountDownLatch(1);
publicstaticvoidmain(String[] args)
throws NoSuchFieldException, IllegalAccessException, InterruptedException { Thread thread1 =newThread(() -> {
threadLocal1.set("thread1-local1");
threadLocal2.set("thread1-local2");
countDownLatch1.countDown();try{
countDownLatch2.await(); }catch(InterruptedException e) {
e.printStackTrace(); } }); Thread thread2 =newThread(() -> {
threadLocal1.set("thread2-local1");
threadLocal2.set("thread2-local2");
countDownLatch1.countDown();try{
countDownLatch2.await(); }catch(InterruptedException e) {
e.printStackTrace(); } }); thread1.start(); thread2.start(); countDownLatch1.await(); System.out.println(threadLocal1 +" "+ threadLocal2);
printThreadLocalMapInfo(thread1); printThreadLocalMapInfo(thread2); countDownLatch2.countDown(); }/**
* 输出相关信息
*/
privatestaticvoidprintThreadLocalMapInfo(Thread thread) throws NoSuchFieldException, IllegalAccessException {
System.out.println("====="+ thread.getName() +"=====");
ObjectthreadLocalMapObject = getThreadLocalMapObject(thread);
System.out.println(threadLocalMapObject); List objects = getEntryList(threadLocalMapObject);
for(Objectobject : objects) {
System.out.println(getEntryKey(object) +" "+ getEntryValue(object));
} }/**
* 获取ThreadLocalMap对象
*/
privatestaticObjectgetThreadLocalMapObject(Thread thread) throws NoSuchFieldException, IllegalAccessException {
Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
threadLocalsField.setAccessible(true);
returnthreadLocalsField.get(thread);
}/**
* 获取ThreadLocalMap对象中的所有Entry
*/
privatestaticList getEntryList(ObjectthreadLocalMapObject)
throws NoSuchFieldException, IllegalAccessException { Field tableField = threadLocalMapObject.getClass().getDeclaredField("table");
tableField.setAccessible(true);
Object[] objects = (Object[]) tableField.get(threadLocalMapObject);
returnArrays.stream(objects).filter((obj) -> {
returnobj !=null;
}).collect(Collectors.toList()); }/**
* 获取Entry的key
*/
privatestaticObjectgetEntryKey(Objectentry) throws NoSuchFieldException, IllegalAccessException {
Field referentField = entry.getClass().getSuperclass().getSuperclass().getDeclaredField("referent");
referentField.setAccessible(true);
returnreferentField.get(entry);
}/**
* 获取Entry的value
*/
privatestaticObjectgetEntryValue(Objectentry) throws NoSuchFieldException, IllegalAccessException {
Field valueField = entry.getClass().getDeclaredField("value");
valueField.setAccessible(true);
returnvalueField.get(entry);
}}
输出结果为:
java.lang.ThreadLocal@31221be2 java.lang.ThreadLocal@377dca04
=====Thread-0=====
java.lang.ThreadLocal$ThreadLocalMap@728938a9java.lang.ThreadLocal@377dca04 thread1-local2java.lang.ThreadLocal@31221be2 thread1-local1=====Thread-1=====
java.lang.ThreadLocal$ThreadLocalMap@25f38edcjava.lang.ThreadLocal@377dca04 thread2-local2java.lang.ThreadLocal@31221be2 thread2-local1
可以看出:Thread类里面的ThreadLocalMap存储着所有ThreadLocal的副本数据。
没有通过ThreadLocal的get方式进行获取数据,而是通过实实在在的通过ThreadLocalMap对象来观察数据。
网友评论