ThreadLocal :每个线程通过此对象都会返回各自的值,互不干扰,这是因为每个线程都存着自己的一份副本。需要注意的是线程结束后,它所保存的所有副本都将进行垃圾回收(除非存在对这些副本的其他引用)

ThreadLocalget操作是这样执行的:ThreadLocalMap map = thread.threadLocals -> return map.getEntry(threadLocal)

ThreadLocalset操作是这样执行的:ThreadLocalMap map = thread.threadLocals -> map.set(threadLocal, value)

三者的关系是:

  • 每个Thread对应的所有ThreadLocal副本都存放在ThreadLocalMap对象中,keyThreadLocalvalue是副本数据
  • ThreadLocalMap对象存放在Thread对象中
  • 通过ThreadLocal获取副本数据时,实际是通过访问Thread来获取ThreadLocalMap,再通过ThreadLocalMap获取副本数据

示例代码如下:

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors; /**
* @author: lihui
* @date: 2020-06-01
*/
public class ThreadLocalStudy { private static final ThreadLocal<String> threadLocal1 = new ThreadLocal<>();
private static final ThreadLocal<String> threadLocal2 = new ThreadLocal<>(); private static CountDownLatch countDownLatch1 = new CountDownLatch(2);
private static CountDownLatch countDownLatch2 = new CountDownLatch(1); public static void main(String[] args)
throws NoSuchFieldException, IllegalAccessException, InterruptedException {
Thread thread1 = new Thread(() -> {
threadLocal1.set("thread1-local1");
threadLocal2.set("thread1-local2");
countDownLatch1.countDown();
try {
countDownLatch2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}); Thread thread2 = new Thread(() -> {
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();
} /**
* 输出相关信息
*/
private static void printThreadLocalMapInfo(Thread thread) throws NoSuchFieldException, IllegalAccessException {
System.out.println("=====" + thread.getName() + "=====");
Object threadLocalMapObject = getThreadLocalMapObject(thread);
System.out.println(threadLocalMapObject);
List<Object> objects = getEntryList(threadLocalMapObject);
for (Object object : objects) {
System.out.println(getEntryKey(object) + " " + getEntryValue(object));
}
} /**
* 获取ThreadLocalMap对象
*/
private static Object getThreadLocalMapObject(Thread thread) throws NoSuchFieldException, IllegalAccessException {
Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
threadLocalsField.setAccessible(true);
return threadLocalsField.get(thread);
} /**
* 获取ThreadLocalMap对象中的所有Entry
*/
private static List<Object> getEntryList(Object threadLocalMapObject)
throws NoSuchFieldException, IllegalAccessException {
Field tableField = threadLocalMapObject.getClass().getDeclaredField("table");
tableField.setAccessible(true);
Object[] objects = (Object[]) tableField.get(threadLocalMapObject);
return Arrays.stream(objects).filter((obj) -> {
return obj != null;
}).collect(Collectors.toList());
} /**
* 获取Entry的key
*/
private static Object getEntryKey(Object entry) throws NoSuchFieldException, IllegalAccessException {
Field referentField = entry.getClass().getSuperclass().getSuperclass().getDeclaredField("referent");
referentField.setAccessible(true);
return referentField.get(entry);
} /**
* 获取Entry的value
*/
private static Object getEntryValue(Object entry) throws NoSuchFieldException, IllegalAccessException {
Field valueField = entry.getClass().getDeclaredField("value");
valueField.setAccessible(true);
return valueField.get(entry);
}
}

输出结果为:

java.lang.ThreadLocal@31221be2 java.lang.ThreadLocal@377dca04
=====Thread-0=====
java.lang.ThreadLocal$ThreadLocalMap@728938a9
java.lang.ThreadLocal@377dca04 thread1-local2
java.lang.ThreadLocal@31221be2 thread1-local1
=====Thread-1=====
java.lang.ThreadLocal$ThreadLocalMap@25f38edc
java.lang.ThreadLocal@377dca04 thread2-local2
java.lang.ThreadLocal@31221be2 thread2-local1
可以看出:Thread类里面的ThreadLocalMap存储着所有ThreadLocal的副本数据。
没有通过ThreadLocal的get方式进行获取数据,而是通过实实在在的通过ThreadLocalMap对象来观察数据。

Java8 API

ThreadLocal Thread ThreadLocalMap 之间的关系的更多相关文章

  1. ThreadLocal和ThreadLocalMap源码分析

    目录 ThreadLocal和ThreadLocalMap源码分析 背景分析 定义 例子 源码分析 ThreadLocalMap源码分析 ThreadLocal源码分析 执行流程总结 源码分析总结 T ...

  2. ThreadLocal与ThreadLocalMap源码分析

    ThreadLocal类 该类主要用于不同线程存储自己的线程本地变量.本文先通过一个示例简单介绍该类的使用方法,然后从ThreadLocal类的初始化.存储结构.增删数据和hash值计算等几个方面,分 ...

  3. 正确理解 AsyncTask,Looper,Handler三者之间的关系(基于android 4.0)

    Looper 和Handler 是理解好AsyncTask的一个基础,我们可以先从这里开始,先给出一个主线程和子线程互相通信的例子. package com.example.loopertest; i ...

  4. Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之间的关系

    转载请包含网址:http://blog.csdn.net/pathuang68/article/details/7351317 一.Surface Surface就是“表面”的意思.在SDK的文档中, ...

  5. (转)C#/.NET主线程与子线程之间的关系

    一般 一个应用程序就对应一个进程,一个进程可有一个或多个线程,而一般有一个主线程.       有的博客上说“至少一个主线程”,这一说法持有怀疑         主线程与子线程之间的关系        ...

  6. [转]C#综合揭秘——细说进程、应用程序域与上下文之间的关系

    引言 本文主要是介绍进程(Process).应用程序域(AppDomain)..NET上下文(Context)的概念与操作.虽然在一般的开发当中这三者并不常用,但熟悉三者的关系,深入了解其作用,对提高 ...

  7. java中paint repaint update 之间的关系

    最近总结了一下java中的paint,repaint和updata三者之间的关系,首先咱们都知道用paint方法来绘图,用repaint重绘,用update来写双缓冲.但是他们之间是怎么来调用的呢,咱 ...

  8. [C#参考]细说进程、应用程序域与上下文之间的关系

    原文转载链接:http://www.cnblogs.com/leslies2/archive/2012/03/06/2379235.html Written by:风尘浪子 引言 本文主要是介绍进程( ...

  9. C#综合揭秘——细说进程、应用程序域与上下文之间的关系

    引言 本文主要是介绍进程(Process).应用程序域(AppDomain)..NET上下文(Context)的概念与操作.虽然在一般的开发当中这三者并不常用,但熟悉三者的关系,深入了解其作用,对提高 ...

随机推荐

  1. thinkphp5.x系列 RCE总结

    Thinkphp  MVC开发模式 执行流程: 首先发起请求->开始路由检测->获取pathinfo信息->路由匹配->开始路由解析->获得模块.控制器.操作方法调度信息 ...

  2. 手把手教你进行Scrapy中item类的实例化操作

    接下来我们将在爬虫主体文件中对Item的值进行填充. 1.首先在爬虫主体文件中将Item模块导入进来,如下图所示. 2.第一步的意思是说将items.py中的ArticleItem类导入到爬虫主体文件 ...

  3. .Net Core微服务化ABP之六——处理Authentication

    上篇中我们已经可以实现sso,并且为各个服务集成sso认证.本篇处理权限系统的角色问题,权限系统分两层,第一层为整体系统角色权限,区分app用户.后台用户.网站用户的接口权限,第二层为业务系统权限,对 ...

  4. jquery判断邮箱及密码是否输入正确的表单提交

    jquery我接触的也不是很多,基本就是照着案例然后查相关方法做出来的,基本用了大概半天的时间,手打加查资料实现.具体如下,首先下载一个jquery包,网址是https://jquery.com/do ...

  5. Linux登录shell和非登录(交互式shell)环境变量配置

    使用Jenkins执行shell脚本的时候, 碰到command not found. 比如java mvn, 这些环境变量配置在/etc/profile 中, 但jenkins执行的时候并没有加载. ...

  6. CentOS7编译和安装GCC7.5

    CentOS7编译和安装GCC7.5 一.    环境介绍: CentOS7 虚拟机连上了互联网(为什么要强调这点呢,因为CentOS7每次进入系统,都需要手动点击右上角的Connect,才能连上互联 ...

  7. java ->基本数据类型与包装类的概述和转化

    基本类型 包装类概述 在实际程序使用中,程序界面上用户输入的数据都是以字符串类型进行存储的.而程序开发中,我们需要把字符串数据,根据需求转换成指定的基本数据类型,如年龄需要转换成int类型,考试成绩需 ...

  8. php5.2安装memcached 扩展

    需要注意版本号,好坑. libmemcached release 1.0.16 - installed from sourcephp-memcached release 2.1.0 - install ...

  9. RN概述

    一.RN概述 中文网:http://reactnative.cn/ ReactNative:使用JS语法编写移动APP应用,RN会把JS转换为底层Java或OC, 最终运行于手机-------完全不依 ...

  10. poj3764字典树路径最大异或和

    The xor-longest Path Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 6853   Accepted: 1 ...