定义
ThreadLocal
是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定的线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。
使用场景
一般来说,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal
。
另一个使用场景是复杂逻辑下的对象传递,比如监听器的传递。
原理
简单原理概述
不同线程访问同一个ThreadLocal
的get
方法,ThreadLocal
内部会从各自的线程中取出一个数组,然后再从数组中根据当前ThradLocal
的索引去查找出对应的 value 值。很显然,不同线程中的数组是不同的,这就是为什么通过ThreadLocal
可以在不同的的线程中维护一套数据的副本并且彼此互不干扰。
内部实现
ThreadLocal
是一个泛型类,它的定义为:public class ThreadLocal<T>
,只要弄清楚ThreadLocal
的get
和set
方法就可以明白它的工作原理;
set(T value)
方法
1 | public void set(T value) { |
在上面set
方法中,首先会通过 values 方法来获取当前线程的ThreadLocal
数据。获取方式很简单:在Thread
类的内部有一个成员专门用于存储线程的ThreadLocal
的数据:ThreadLocal.Values localValues
,因此获取当前线程的ThreadLocal
数据就变得异常简单了。如果localValues
的值为 null,那么久需要对其进行初始化,初始化后再将ThreadLocal
的值进行存储。在localValues
内部有一个数组:private Object[] table
,ThreadLocal
的值就存在在这个table
数组中。源码如下:
1 | /** |
从上面源码可以看出来:ThreadLocal
的值在table
数组中的存储位置总是为ThradLocal
的reference
字段所标识的对象的下一个位置,比如ThreadLocal
的reference
对象在table
数组中的索引为index
,那么ThreadLocal
的值在table
数组中的索引就是index+1
。最终ThreadLocal
的值将会被存储在table
数组中:table[index+1]=value
。
get()
方法
1 | public T get() { |
get()
方法的逻辑:取出当前线程的localValues
对象,如果这个对象为 null 那么就返回初始值,初始值由ThreadLocal
的initialValue
方法来描述,默认情况下为 null,可以重写该方法,返回自定义的值。如果localValues
对象不为 null,那么就取出它的table
数组并找出ThreadLocal
的reference
对象在table
数组中的位置,然后table
数组中的下一个位置所存储的数据就是ThreadLocal
的值。
总结
从ThreadLocal
的set
和get
方法可以看出,它们所操作的对象都是当前线程的localVaules
对象的table
数组,因此在不同线程中访问同一个ThreadLocal
的set
和get
方法,它们对ThreadLocal
所做的读/写操作仅限于各自线程的内部,这就是为什么ThreadLocal
可以在多个线程中互不干扰地存储和修改数据。