GitHub Page: http://blog.cloudli.top/posts/Java-ThreadLocal-的使用与源码解析/

ThreadLocal 主要解决的是每个线程绑定自己的值,可以将 ThreadLocal 看成全局存放数据的盒子,盒子中存储每个线程的私有数据。

验证线程变量的隔离性

  1. import static java.lang.System.out;
  2. public class Run {
  3. private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
  4. static class Work extends Thread {
  5. @Override
  6. public void run() {
  7. threadLocal.set(0);
  8. for (int i = 1; i <= 5; i++) {
  9. // 获取数据
  10. int sum = threadLocal.get();
  11. out.printf("%s's sum = %s\n", getName(), threadLocal.get());
  12. sum += i;
  13. // 写回数据
  14. threadLocal.set(sum);
  15. }
  16. out.printf("END %s's sum = %d\n\n", getName(), threadLocal.get());
  17. }
  18. }
  19. public static void main(String[] args) {
  20. Work work1 = new Work(),
  21. work2 = new Work();
  22. work1.start();
  23. work2.start();
  24. }
  25. }

运行结果:

  1. Thread-0's sum = null
  2. Thread-1's sum = null
  3. Thread-1's sum = 1
  4. Thread-1's sum = 3
  5. Thread-1's sum = 6
  6. Thread-1's sum = 10
  7. END Thread-1's sum = 15
  8. Thread-0's sum = 1
  9. Thread-0's sum = 3
  10. Thread-0's sum = 6
  11. Thread-0's sum = 10
  12. END Thread-0's sum = 15
  13. Process finished with exit code 0

从结果来看,两个线程的计算结果一致,ThreadLocal 中隔离了两个线程的数据。

ThreadLocal 源码解析

ThreadLocalMap 内部类

ThreadLocal 中有一个 ThreadLocalMap 内部类,所以 ThreadLocal 实际上是使用一个哈希表来存储每个线程的数据的。

ThreadLocalMapHashMap 不同,其中 Entry 是一个弱引用,这意味着每次垃圾回收运行时都会将储存的数据回收掉。而且它只使用了数组来存储键值对。

ThreadLocalMap 中的 Entry

  1. static class Entry extends WeakReference<ThreadLocal<?>> {
  2. /** The value associated with this ThreadLocal. */
  3. Object value;
  4. Entry(ThreadLocal<?> k, Object v) {
  5. super(k);
  6. value = v;
  7. }
  8. }

get() 方法

  1. public T get() {
  2. // 得到当前线程
  3. Thread t = Thread.currentThread();
  4. // 获取当前线程的哈希表
  5. ThreadLocalMap map = getMap(t);
  6. if (map != null) {
  7. // 从哈希表中获取当前线程的数据
  8. ThreadLocalMap.Entry e = map.getEntry(this);
  9. if (e != null) {
  10. @SuppressWarnings("unchecked")
  11. T result = (T)e.value;
  12. return result;
  13. }
  14. }
  15. return setInitialValue();
  16. }

get() 方法首先得到当前线程,然后获取当前线程的 ThreadLocalMap 对象,然后从中取出数据。

这里的 map.getEntry(this) 看起来很奇怪,在前面有这样一行代码:

  1. ThreadLocalMap map = getMap(t);

这个方法获取当前线程的 ThreadLocalMap 对象,所以,虽然 map.getEntry() 中的 key 总是 ThreadLocal 对象本身,但是每个线程都持有有自己的 ThreadLocalMap 对象。

getMap() 方法

  1. /**
  2. * Get the map associated with a ThreadLocal. Overridden in
  3. * InheritableThreadLocal.
  4. *
  5. * @param t the current thread
  6. * @return the map
  7. */
  8. ThreadLocalMap getMap(Thread t) {
  9. return t.threadLocals;
  10. }

看到这个方法,get() 方法中 map.getEntry(this) 的迷雾就解开了。这里可以看到返回的是线程中的 threadLocals 属性。那么这里瞟一眼 Thread 的源码:

  1. /* ThreadLocal values pertaining to this thread. This map is maintained
  2. * by the ThreadLocal class. */
  3. ThreadLocal.ThreadLocalMap threadLocals = null;

其实每次 get() 时都是先获取了线程自己的 ThreadLocalMap 对象,然后对这个对象进行操作。

set() 方法

  1. public void set(T value) {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t);
  4. if (map != null)
  5. map.set(this, value);
  6. else
  7. // 为当前线程创建一个 ThreadLocalMap 对象
  8. createMap(t, value);
  9. }

set() 方法也是先获取当前线程自己的 ThreadLocalMap 对象,然后再设置数据。如果获取的哈希表为 null,则创建一个。

createMap() 方法

  1. void createMap(Thread t, T firstValue) {
  2. t.threadLocals = new ThreadLocalMap(this, firstValue);
  3. }

createMap() 方法创建一个 ThreadLocalMap 对象,该对象由线程持有。

总结

  • ThreadLocal 可以隔离线程的变量,每个线程只能从这个对象中获取到属于自己的数据。
  • ThreadLocal 使用哈希表来存储线程的数据,而且这个哈希表是由线程自己持有的,每次获取和设值都会先获取当前线程持有的ThreadLocalMap 对象。
  • ThreadLocalMap 中的 key 总是 ThreadLocal 对象本身。
  • ThreadLocalMap 中的 Entry 是弱引用,每次 GC 运行都会被回收。

Java ThreadLocal 的使用与源码解析的更多相关文章

  1. 死磕 java同步系列之ReentrantReadWriteLock源码解析

    问题 (1)读写锁是什么? (2)读写锁具有哪些特性? (3)ReentrantReadWriteLock是怎么实现读写锁的? (4)如何使用ReentrantReadWriteLock实现高效安全的 ...

  2. 死磕 java同步系列之CyclicBarrier源码解析——有图有真相

    问题 (1)CyclicBarrier是什么? (2)CyclicBarrier具有什么特性? (3)CyclicBarrier与CountDownLatch的对比? 简介 CyclicBarrier ...

  3. 死磕 java同步系列之Phaser源码解析

    问题 (1)Phaser是什么? (2)Phaser具有哪些特性? (3)Phaser相对于CyclicBarrier和CountDownLatch的优势? 简介 Phaser,翻译为阶段,它适用于这 ...

  4. 死磕 java同步系列之StampedLock源码解析

    问题 (1)StampedLock是什么? (2)StampedLock具有什么特性? (3)StampedLock是否支持可重入? (4)StampedLock与ReentrantReadWrite ...

  5. 死磕 java同步系列之Semaphore源码解析

    问题 (1)Semaphore是什么? (2)Semaphore具有哪些特性? (3)Semaphore通常使用在什么场景中? (4)Semaphore的许可次数是否可以动态增减? (5)Semaph ...

  6. 死磕 java同步系列之ReentrantLock源码解析(二)——条件锁

    问题 (1)条件锁是什么? (2)条件锁适用于什么场景? (3)条件锁的await()是在其它线程signal()的时候唤醒的吗? 简介 条件锁,是指在获取锁之后发现当前业务场景自己无法处理,而需要等 ...

  7. [Java多线程]-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  8. java容器三:HashMap源码解析

    前言:Map接口 map是一个存储键值对的集合,实现了Map接口的主要类有以下几种 TreeMap:用红黑树实现 HashMap:数组和链表实现 HashTable:与HashMap类似,但是线程安全 ...

  9. [Java多线程]-Thread和Runable源码解析

    多线程:(百度百科借一波定义) 多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提 ...

随机推荐

  1. java架构之路-(源码)mybatis基本使用

    我们今天先来简单了解一下我们持久层框架,mybatis的使用.而且现在的注解成为趋势,我主要说一下注解方向的使用吧(配置文件也会说) 从使用角度只要是三个部分,mybatis-config.xml,m ...

  2. FreeSql 新手上路系列教程已发布在 cnblogs

    FreeSql 是一个功能强大的对象关系映射程序(O/RM),支持 .NETCore 2.1+ 或 .NETFramework 4.5+(QQ群:4336577) FreeSql采用MIT开源协议托管 ...

  3. Winform中对ZedGraph的曲线标签进行设置,比如去掉标签边框

    场景 Winforn中设置ZedGraph曲线图的属性.坐标轴属性.刻度属性: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/10 ...

  4. charles 帮助菜单总结

    本文参考:charles 帮助菜单总结 charles的window和help的菜单介绍 其中window菜单的如下 这里我一般用到的就是 Active connections:可以用它来看charl ...

  5. 39 (OC) 瀑布流、不规则UI

    39  (OC)  瀑布流.不规则UI

  6. Bean 装配,从 Spring 到 Spring Boot

    目录  从SSM的集成谈到Bean的装配  Bean的装配 由XML到Java Config 自动扫描 Bean的注入 SSM集成的Java版 Spring Boot Magic Auto Confi ...

  7. abp(net core)+easyui+efcore实现仓储管理系统——EasyUI之货物管理一 (十九)

    abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...

  8. ASP.NET Core 3.0 : 二十五. TagHelper

    什么是TagHelper?这是ASP.NET Core 中新出现的一个名词,它的作用是使服务器端代码可以在Razor 文件中参与创建和呈现HTML 元素.(ASP.NET Core 系列目录) 一.概 ...

  9. 利用Python进行数据分析:【IPython】

    一.IPython基础功能 1.IPython是交互式的Python命令行2.安装与使用 #安装:pip install ipython #使用:ipython与Python解释器的使用方法一致 注: ...

  10. Python 编译器与解释器

    Python 编译器与解释器 Python的环境我们已经搭建好了,可以开始学习基础知识了.但是,在此之前,还要先说说编译器与解释器相关的内容. 如果这部分内容,让你觉得难以理解或不能完全明白,可以暂时 ...