JDK 之 ThreadLocal 源码阅读笔记
Contents
ThreadLocal 类声明
public class ThreadLocal<T>
可以看到,它只是单纯的一个泛型类,没有其他特别的修饰符。
看注释可以看到,它通常的使用方式是在类字段添加:
private static final ThreadLocal...
属性
- private final int threadLocalHashCode = nextHashCode(); ThreadLocal 的 hashCode 变量
- private static AtomicInteger nextHashCode = new AtomicInteger(); 原子计数器
- private static final int HASH_INCREMENT = 0x61c88647; 每个哈希值之间的间距值
可以看到,这三个东西共同控制每一轮的哈希值,而且hash 值的规律为:
0
0x61c88647
0x61c88647 * 2
0x61c88647 * 3
0x61c88647 * 4
即 下一轮的 hashCode - 上一轮的 hashCode = 0x61c88647
构造函数
只有一个
public ThreadLocal() {
}
方法
- protected T initialValue(); 这个方法是用来进行初始化的。可以看到,它的默认值为 null
public T get(); 获取当前线程持有的ThreadLocal的泛型 T 的值。
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
通过该方法的源码可知:上面的 initialValue() 方法,是在第一次 get 的时候,并且 ThreadLocalMap 还没有被初始化的时候调用的( initialValue() 仅在 get 方法被调用 ) 注意,它的 key 为 this 因为可能会有多个 ThreadLocal 对象,this 就保证了,每个 ThreadLocal 都是从它自身获取相应的类型的值。
public void set(T value);
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
可以看到,set 方法也会进行 null 的判断,如果还没有初始化的话,则直接以参数中的值来进行初始化,而不会调用 initialValue() 方法,如果要依赖于 initialValue() 方法进行某种业务逻辑操作的话,这点要特别注意。
public void remove(); 删除当前线程所拥有的 ThreadLocal
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
void createMap(Thread t, T firstValue); 创建 ThreadLocalMap
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
可以看到, threadLocals 它是 Thread 对象的属性来的。正是用它来为每个 Thread 保存各自的 线程私有的局部变量
ThreadLocal.ThreadLocalMap threadLocals = null
- public static
ThreadLocalwithInitial(Supplier<? extends S> supplier);
这个静态初始化方法,提供了另一种初始化值的方式。(从 JDK 1.8 才开始),参数是接口 Supplier ,它的接口为:
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
内部类
SuppliedThreadLocal
它的构造函数参数为 Supplier 接口,提供了另一种 ThreadLocal 初始化值的方式,用在 ThreadLocal 的静态方法 public static ThreadLocal 。 withInitial(Supplier<? extends S> supplier)
它的作用相当于:
private static final ThreadLocal<Integer> thread = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return super.initialValue();
}
};
只是,它将获取初始值的方法,以接口的形式来提供,方便进行一些较复杂的初始化方式。
ThreadLocalMap
这个就是真正的 ThreadLocal 持有类。
Entry
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
每一个元素实体类,用于持有 ThreadLocal 所对应的值(key 为 ThreadLocal, value 为 ThreadLocal 所对应的泛型的类型)。
属性及说明
- private static final int INITIAL_CAPACITY = 16; 初始值容量,必须为2的倍数
- private Entry[] table; Entry 类的数组,维护各个ThreadLocal及其拥有的 value
- private int size = 0; 实际的元素个数
private int threshold; 重新安排 table 表的 entry 位置。可以通过方法 setThreshold ,但实际上的 threshold 是参数的 2⁄3
private void setThreshold(int len) { threshold = len * 2 / 3; }
方法
rehash() ;
if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash();
可以看到 size >= threshold 是必要非充分的 rehash 条件.
resize(); 扩容
if (size >= threshold - threshold / 4) resize();
条件是 size >= threshold - threshold/4
以上这些并不能由我们自行干扰的,而是 JDK 开发者处理的(当然,也可以自己修改 JDK 源码,然后自行编译~)
总结
ThreadLocal 类,主要就是维护每个 Thread 中的 ThreadLocal.ThreadLocalMap threadLocals = null; 属性的值及状态。
即每一条线程,都有一份自己的 threadLocals, ThreadLocal 只是提供了一个统一的 API,让你 get, set, init 等操作。其中 key 就是 this 对象(即每一个当前的 ThreadLocal 对象自身),value 就是当前 this 对象所持有的对应的 ThreadLocal 的泛型类型。
比如下面代码
package org.agoncal.sample.jmh;
public class Test {
private static final ThreadLocal<Integer> thread = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return super.initialValue();
}
};
private static final ThreadLocal<String> thread2 = new ThreadLocal<String>(){
@Override
protected String initialValue() {
return super.initialValue();
}
};
public static void main(String[] args) {
System.out.println(thread.get());
System.out.println(thread2.get());
}
}
这里有两个 ThreadLocal 对象,所以
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocalMap 的大小就是2,key 就是 *this*,this 就是指当前调用 api 的 ThreadLocal 对象。value 就是当前 ThreadLocal 对应的泛型的类型的值。
以上面的代码为例子:
thread.get()
在调用的时候,Thread.currentThread() 来获取当前线程 t,然后通过 t来获取该当前线程持有的 ThreadLocalMap (它包含了 thread 和 thread2 为 key 的对象,value 就分别就是它们各自对应的值)。
在这里,this 就是指 thread 对象,它的 value 就是 Integer 类型。
同理,如果是 thread2 调用的话,this 就是指 thread2 ,它的 value 就是 String 类型。
因为该对象表示的是每一条线程自身的数据,并不涉及多线程的共享问题,所以这里是没有同步之类的要求。