ThreadLocal Thread ThreadLocalMap 之间的关系

简介: ThreadLocal :每个线程通过此对象都会返回各自的值,互不干扰,这是因为每个线程都存着自己的一份副本。需要注意的是线程结束后,它所保存的所有副本都将进行垃圾回收(除非存在对这些副本的其他引用)ThreadLocal的get操作是这样执行的:ThreadLocalMap map = thread.threadLocals -> return map.getEntry(threadLocal)ThreadLocal的set操作是这样执行的:ThreadLocalMap map = thread.threadLocals -> map.set(threadLocal, value)

image.png


前言

分割线.jpg

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对象来观察数据。


相关文章
|
4月前
|
存储 安全 Java
|
4月前
|
Java 关系型数据库 MySQL
【数据库连接,线程,ThreadLocal三者之间的关系】
【数据库连接,线程,ThreadLocal三者之间的关系】
54 0
|
4月前
|
存储 安全 Java
synchronized 与多线程的哪些关系
synchronized 与多线程的哪些关系
42 0
多线程之Thread类常见方法及线程的状态
多线程之Thread类常见方法及线程的状态
|
Java
多线程的创建的方式一:继承于Thread类
多线程的创建的方式一:继承于Thread类
43 0
|
安全 Java 调度
多线程(三):Thread 类的基本属性
多线程(三):Thread 类的基本属性
109 0
多线程(三):Thread 类的基本属性
理清ThreadLocal、ThreadLocalMap、Thread之间的关系
理清ThreadLocal、ThreadLocalMap、Thread之间的关系
123 0
|
Java
线程和进程概念区别—及线程常用方法和状态
进程和线程是操作系统中的两个基本概念。 进程是程序执行的基本单位,每个进程都有自己独立的内存空间和系统资源,它拥有自己的虚拟地址空间、代码段、数据段、堆栈段等。一个程序可以对应多个进程,每个进程之间是独立运行的,互相之间不会影响。
267 0
ThreadLocal 父子线程之间该如何传递数据?
ThreadLocal 父子线程之间该如何传递数据?
|
Java 调度
Java多线程的创建与Thread类的方法及使用(上)
Java多线程的创建与Thread类的方法及使用(上)
Java多线程的创建与Thread类的方法及使用(上)