ThreadLocal Thread ThreadLocalMap 之间的关系
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获取副本数据
示例代码如下:
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对象来观察数据。
ThreadLocal Thread ThreadLocalMap 之间的关系的更多相关文章
- ThreadLocal和ThreadLocalMap源码分析
目录 ThreadLocal和ThreadLocalMap源码分析 背景分析 定义 例子 源码分析 ThreadLocalMap源码分析 ThreadLocal源码分析 执行流程总结 源码分析总结 T ...
- ThreadLocal与ThreadLocalMap源码分析
ThreadLocal类 该类主要用于不同线程存储自己的线程本地变量.本文先通过一个示例简单介绍该类的使用方法,然后从ThreadLocal类的初始化.存储结构.增删数据和hash值计算等几个方面,分 ...
- 正确理解 AsyncTask,Looper,Handler三者之间的关系(基于android 4.0)
Looper 和Handler 是理解好AsyncTask的一个基础,我们可以先从这里开始,先给出一个主线程和子线程互相通信的例子. package com.example.loopertest; i ...
- Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之间的关系
转载请包含网址:http://blog.csdn.net/pathuang68/article/details/7351317 一.Surface Surface就是“表面”的意思.在SDK的文档中, ...
- (转)C#/.NET主线程与子线程之间的关系
一般 一个应用程序就对应一个进程,一个进程可有一个或多个线程,而一般有一个主线程. 有的博客上说“至少一个主线程”,这一说法持有怀疑 主线程与子线程之间的关系 ...
- [转]C#综合揭秘——细说进程、应用程序域与上下文之间的关系
引言 本文主要是介绍进程(Process).应用程序域(AppDomain)..NET上下文(Context)的概念与操作.虽然在一般的开发当中这三者并不常用,但熟悉三者的关系,深入了解其作用,对提高 ...
- java中paint repaint update 之间的关系
最近总结了一下java中的paint,repaint和updata三者之间的关系,首先咱们都知道用paint方法来绘图,用repaint重绘,用update来写双缓冲.但是他们之间是怎么来调用的呢,咱 ...
- [C#参考]细说进程、应用程序域与上下文之间的关系
原文转载链接:http://www.cnblogs.com/leslies2/archive/2012/03/06/2379235.html Written by:风尘浪子 引言 本文主要是介绍进程( ...
- C#综合揭秘——细说进程、应用程序域与上下文之间的关系
引言 本文主要是介绍进程(Process).应用程序域(AppDomain)..NET上下文(Context)的概念与操作.虽然在一般的开发当中这三者并不常用,但熟悉三者的关系,深入了解其作用,对提高 ...
随机推荐
- java的++i和i++的分析
关于自增自减,先来一道题,大家尝试着做一下: public static void main(String[] args) { int a=2; int b=a++; int c=--a; Syste ...
- Kubernetes笔记(二):了解k8s的基本组件与概念
前文 Kubernetes笔记(一):十分钟部署一套K8s环境 介绍了如何快速搭建一个k8s系统.为了继续使用k8s来部署我们的应用,需要先对k8s中的一些基本组件与概念有个了解. Kubernete ...
- 硬肝4.4w字为你写成Java开发手册
先来看一下本篇文章的思维导图吧,我会围绕下面这些内容进行讲解.内容很干,小伙伴们看完还希望不吝转发.(高清思维导图版本关注作者公众号 Java建设者 回复 Java666 获取,其他思维导图获取方式在 ...
- Spring官网阅读(十)Spring中Bean的生命周期(下)
文章目录 生命周期概念补充 实例化 createBean流程分析 doCreateBean流程分析 第一步:factoryBeanInstanceCache什么时候不为空? 第二步:创建对象(crea ...
- Alink漫谈(二) : 从源码看机器学习平台Alink设计和架构
Alink漫谈(二) : 从源码看机器学习平台Alink设计和架构 目录 Alink漫谈(二) : 从源码看机器学习平台Alink设计和架构 0x00 摘要 0x01 Alink设计原则 0x02 A ...
- MySQL 入门(4):锁
摘要 在这篇文章中,我将从上一篇的一个小例子开始,跟你介绍一下InnoDB中的行锁. 在这里,会涉及到一个概念:两阶段加锁协议. 之后,我会介绍行锁中的S锁和X锁,以及这两种锁的作用. 但是我们会发现 ...
- MATLAB与三大变换
运行 Simulink 有三种方式: z 在 MATLAB 的命令窗口直接键入“Simulink”并回车: z 单击 MATLAB 工具条上的 Simulink 图标: z 在 MATLAB 菜单上选 ...
- 常见的GAN网络的相关原理及推导
常见的GAN网络的相关原理及推导 在上一篇中我们给大家介绍了GAN的相关原理和推导,GAN是VAE的后一半,再加上一个鉴别网络.这样而导致了完全不同的训练方式. GAN,生成对抗网络,主要有两部分构成 ...
- Centos ps命令
输出格式(ps -aux) USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND VSZ:占用的虚拟内存大小() RSS: COMMAND: 执 ...
- 移动端APP自动化测试超全基础汇总
目录 一.面试过程 1.自动化岗位要求 2.面试流程,面试类型 3.沟通技巧,不同级别要求 二.真实面试案例 1.一个输入框的面试题(有人拿到高级岗位,有人连初级都没拿到,为什么) 三.自我分析 1. ...