ThreadLocal
是什么
ThreadLocal 是 JDKjava.lang
包中的一个用来实现相同线程数据共享不同的线程数据隔离的一个工具。 我们来看下 JDK 源码中是如何解释的:
大致的意思是
一句话说就是 ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用(相同线程数据共享),也就是变量在线程间隔离(不同的线程数据隔离)而在方法或类间共享的场景。
ThreadLocal
使用
我们先通过两个例子来看一下 ThreadLocal
的使用
例子 1 普通变量
程序的运行的随机结果如下:
从结果我们可以看出多个线程在访问同一个变量的时候出现的异常,线程间的数据没有隔离。下面我们来看下采用ThreadLocal
变量的方式来解决这个问题的例子。
例子 2ThreadLocal
变量
程序运行结果
从结果来看,这次我们很好的解决了多线程之间数据隔离的问题,十分方便。
这里可能有的朋友会觉得在例子 1 中我们完全可以通过加锁来实现这个功能。是的没错,加锁确实可以解决这个问题,但是在这里我们强调的是线程数据隔离的问题,并不是多线程共享数据的问题。假如我们这里除了getString()
之外还有很多其他方法也要用到这个 String,这个时候各个方法之间就没有显式的数据传递过程了,都可以直接中 ThreadLocal
变量中获取,这才是 ThreadLocal
的核心,相同线程数据共享不同的线程数据隔离。
由于ThreadLocal
是支持泛型的,这里采用的是存放一个 String
来演示,其实可以存放任何类型,效果都是一样的。
ThreadLocal
源码分析
在分析源码前我们明白一个事那就是对象实例与 ThreadLocal
变量的映射关系是由线程 Thread
来维护的,对象实例与 ThreadLocal
变量的映射关系是由线程 Thread
来维护的,对象实例与 ThreadLocal
变量的映射关系是由线程 Thread
来维护的。重要的事情说三遍。换句话说就是对象实例与 ThreadLocal
变量的映射关系是存放的一个Map
里面(这个Map
是个抽象的Map
并不是java.util
中的Map
),而这个Map
是 Thread
类的一个字段!而真正存放映射关系的Map
就是ThreadLocalMap
。下面我们通过源码的中几个方法来看一下具体的实现。
在 set
方法中首先获取当前线程,然后通过 getMap
获取到当前线程的 ThreadLocalMap
类型的变量 threadLocals
,如果存在则直接赋值,如果不存在则给该线程创建 ThreadLocalMap
变量并赋值。赋值的时候这里的 this
就是调用变量的对象实例本身。
get
方法也比较简单,同样也是先获取当前线程的ThreadLocalMap
变量,如果存在则返回值,不存在则创建并返回初始值。
ThreadLocalMap
源码分析
ThreadLocal
的底层实现都是通过ThreadLocalMap
来实现的,我们先看下ThreadLocalMap
的定义,然后再看下相应的 set
和get
方法。
ThreadLocalMap
中使用 Entry[]
数组来存放对象实例与变量的关系,并且实例对象作为 key,变量作为 value 实现对应关系。并且这里的 key 采用的是对实例对象的弱引用,(因为我们这里的 key 是对象实例,每个对象实例有自己的生命周期,这里采用弱引用就可以在不影响对象实例生命周期的情况下对其引用)。
至此我们看完了ThreadLocal
相关的 JDK 源码,我自己也有了更深入的了解,也希望能帮助到大家。
小结
在平时忙碌的工作中我们经常解决的是一个业务的需求,往往很少会涉及到底层的源码或者框架的具体实现代码。 其实这是很不好的,其实很多的东西的原理都是一样的,我们需要经常去看一下源码,了解一些底层的实现,不能总是停留在表层,代码看到多了,才能写出好的代码,并且还能学到很多东西。 随着我们知道的越来越多,我们会发现我们不知道的也越来越多。加油,共勉!