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 ThreadLocal withInitial(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 是参数的 23

        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 类型。

因为该对象表示的是每一条线程自身的数据,并不涉及多线程的共享问题,所以这里是没有同步之类的要求。