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. Jmeter的简单使用

    前言 对于jmeter的使用有很多内容,本章节只是简单介绍jmeter的两个方面的内容:一个是使用jmeter模拟postman发送http请求,一个是使用jmete进行压力测试. 更多的内容请参考官 ...

  2. HashMap 底层探索

    其实HashMap就是一个Node数组,只是这个数组很奇怪它的每一个Node节点都有自己的下一个Node;这个是hashMap的Node的源码: static class Node<K,V> ...

  3. 11_JavaScript基础入门(1)

    JavaScript简介 1.JavaScript用途 前端三层: 结构层  HTML(从语义的角度描述页面的结构) 样式层  CSS(从审美的角度装饰页面) 行为层  JavaScript (从交互 ...

  4. 读源码之Spring 核心内容

    为什么有这篇文档 工作两三年之后,总感觉什么东西都懂,但是什么东西又都不会.所以日常学习是很有必要的,俗话说学而不思则罔 ,思而不学则殆.所以我们要学思结合,学习的方法有很多,但是思考的深度或者说有没 ...

  5. 一些常见的pro文件配置

    UI_DIR = ./ui #ui文件目录 TARGET = Test #最终生成目标名 DESTDIR = $$PWD/../test #目标生成目录,$$PWD表示当前目录下 DLLDESTDIR ...

  6. CF-448C Painting Fence 分治

    Painting fence 题意 乍一看以为是之前做过的一道单调队列优化的DP,不是. 也是有n块木板,每个木板宽1米,有一个高度ai,现在要把他们刷成橘色,给了你一个宽一米的刷子,你可以横着刷,或 ...

  7. JDBC04 PreparedStatement

    PreparedStatement类 存在预编译,用占位符去填参数(参数索引从1开始算),可以防止SQL注入 try { Class.forName("com.mysql.cj.jdbc.D ...

  8. 【hdu1007】最近点对

    http://acm.hdu.edu.cn/showproblem.php?pid=1007 分治法的经典应用,复杂度可以证明为nlognlogn #include <iostream> ...

  9. 网络通信-在浏览器输入url,基于TCP/IP协议,浏览器渲染的解释

    知识点1: 网络模型 TCP/IP四层 和ISO七层模型 (统一省略后面层字.比如传输代表传输层) 知识点2: 在应用层中TCP建立连接,经历的三次握手协议 首先:,TCP协议是什么? 为什么要三次握 ...

  10. yum安装mysql 之后问题

    日志报错: 190412 15:56:50 [ERROR] Can't open the mysql.plugin table. Please run mysql_upgrade to create ...