源码关于类的注释初识
源码在类的定义之前有一段注释,大体是这个意思:
ThreadLocal类提供 thread-local variabels(线程局部变量)
这些变量与普通变量不同,每个线程都可以通过其 get 或 set方法来访问自己的独立初始化的变量副本
ThreadLocal的实例通常在类中是private static的一个域,这个域维持与线程相关联的某种状态(例如用户ID或事务ID)
注释中给出了这样一个例子:
import java.util.concurrent.atomic.AtomicInteger; public class ThreadId { // Atomic integer containing the next thread ID to be assigned private static final AtomicInteger nextId = new AtomicInteger(0); // Thread local variable containing each thread's ID private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return nextId.getAndIncrement(); } }; // Returns the current thread's unique ID, assigning it if necessary public static int get() { return threadId.get(); } }
ThreadId这个类对于每个线程都生成一个唯一本地标识(线程id值),这个id在第一次被调用后分配,
并在随后的调用中保持不变
关键代码
public class ThreadLocal<T> { /** * 内部类ThreadLocalMap中使用,相当于HashMap中的hashCode设计
* ThradLocalMap类设计了一个静态内部类Entry,其构造方法为(ThreadLocal<?> k,Object v)
* 搜索Entry时,根据k.threadLocalHashCode值做下标寻址计算 */ private final int threadLocalHashCode = nextHashCode(); /** * The next hash code to be given out. Updated atomically. Starts at zero. */ private static AtomicInteger nextHashCode = new AtomicInteger(); /** * hash增长值,设计为7
* */ private static final int HASH_INCREMENT = 0x61c88647; /** * 获取下一个hash值 */ private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); } /**
* 设置ThreadLocal初始值,第一次调用get()方法获取初始值的时候将调用此方法
* 正常情况下,该方法最多只会被一个线程调用一次,但是如果后续调用了remove()方法接着调用get()时,
* 该方法也会被调用 * 默认返回是null,如果想设置为非null值,在创建ThreadLocal时使用匿名内部类覆盖此方法*/ protected T initialValue() { return null; } /** * lambda方式初始化,比使用匿名内部类覆盖initialValue()更易理解
* ThreadLocal<String> local = ThreadLocal.withInitial(()->"hello")
*/ public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) { return new SuppliedThreadLocal<>(supplier); } /** * 构造方法 * @see #withInitial(java.util.function.Supplier) */ public ThreadLocal() { } /*** 核心方法,获取当前线程变量的值 * @return the current thread's value of this thread-local */ 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(); }/** * 核心方法,设置本地线程变量的值,支持泛型*/ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } /** * 清除变量值*/ public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); } /** * 核心逻辑之一,查看Thread.java源码可以看到
* Thread类持有一个ThreadLocal.ThreadLocalMap类型的变量threadLocals*/ ThreadLocalMap getMap(Thread t) { return t.threadLocals; } /** * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the map */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }/**
* ThreadLocal设计的一个关键部分,是它的一个静态内部类 * ThreadLocalMap的方法,除了构造方法全部是private的,因此没有任何操作会溢出ThreadLocal
* 这是一个HashMap结构
* 查看set方法:set(ThreadLocal<?> key,Object value),可以知道这个map以ThreadLocal对象作为key
* 但是搜索下标,使用的是key.threadLocalHashCode做计算 */ static class ThreadLocalMap {
//Entry是ThreadLocalMap的一个静态内部类
private Entry[] table; ………… } }
使用场景
同一个线程内 不同逻辑间需要共享数据(但又无法通过传值来共享数据),或者在同一个线程内为避免重复创建对象 而希望数据重用等情况
注意
对一同一个线程的不同ThreadLocal来讲,这些ThreadLocal共享一个table数组,每个ThreadLocal实例在数组中的索引不同