Threadlocal 魔法
ThreadLocal
详解
前言
对于 ThreadLocal
的使用,并不难,这次主要讲述 ThreadLocal
的实现方式以及原理
ThreadLocal
是什么
ThreadLocal
为解决多线程并发问题提供的一种新的思路。
当使用 ThreadLocal
维护变量的时候,ThreadLocal
为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立修改自己的副本,而不会修改到其他人的变量副本。
从线程角度看,Local
即本地意思,目标变量就像是线程的本地变量。
原理
ThreadLocal
是连接 Thread
与 ThreadLocalMap
粘合剂,是用来处理 Thread
的 ThreadLocalMap
属性,
包括 initialValue() 变量,set 对应的变量,get 对应的变量。
ThreadLocalMap
用来存储数据,采用类似HashMap
的机制,存储了以ThreadLocal
为Key
,目标数据为Value
的Entry
键值对数组结构。
Thread
有个 ThreadLocalMap 的属性,存储的数据存放在此处。
Thread
、ThreadLocal
、 ThreadLocalMap
的关系
ThreadLocalMap
是 ThreadLocal
的内部类,有 ThreadLocal
创建,Thread
有 ThreadLocal.ThreadLocalMap
类型的属性,源码如下
Thread
public class Thread implements Runnable {
/*
* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class.
*/
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
ThreadLocal
和 ThreadLocalMap
public class ThreadLocal<T> {
/**
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread. To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.
*/
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
}
ThreadLocal
为 Thread
的 ThreadLocalMap
进行赋值
/**
* 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 出来,以当前 ThreadLocal 的对象为 key ,以目标值为 value 存入
ThreadLocal
核心的方法
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
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(T)
设置当前线程的局部变量,以当前的ThreadLocal
对象 为键,目标变量对象为值存入当前线程的ThreadLocalMap
对象中- 在获取当前线程的
ThreadLocalMap
的时候,不存在的时候会创建一个ThreadLocalMap
,也就是说,Thread
的ThreadLocal.ThreadLocalMap
对象是由ThreadLocal
实例化处理的
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @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();
}
get()
为从ThreadLocalMap
中获取值,如果ThreadLocalMap
不存在,则走setInitialValue
方式,否则取出之前设置的值
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
/**
* Returns the current thread's "initial value" for this
* thread-local variable. This method will be invoked the first
* time a thread accesses the variable with the {@link #get}
* method, unless the thread previously invoked the {@link #set}
* method, in which case the {@code initialValue} method will not
* be invoked for the thread. Normally, this method is invoked at
* most once per thread, but it may be invoked again in case of
* subsequent invocations of {@link #remove} followed by {@link #get}.
*
* This implementation simply returns {@code null}; if the
* programmer desires thread-local variables to have an initial
* value other than {@code null}, {@code ThreadLocal} must be
* subclassed, and this method overridden. Typically, an
* anonymous inner class will be used.
*
* @return the initial value for this thread-local
*/
protected T initialValue() {
return null;
}
initialValue()
返回了该线程变量的初始值,是一个保护方法,为了让子类覆盖此方法,- 此方法只执行一次,是一个延迟方法,在调用
set
或get
的时候创建出来
/**
* Creates a thread local variable. The initial value of the variable is
* determined by invoking the {@code get} method on the {@code Supplier}.
*
* @param the type of the thread local's value
* @param supplier the supplier to be used to determine the initial value
* @return a new thread local variable
* @throws NullPointerException if the specified supplier is null
* @since 1.8
*/
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
return new SuppliedThreadLocal<>(supplier);
}
/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
/**
* Removes the current thread's value for this thread-local
* variable. If this thread-local variable is subsequently
* {@linkplain #get read} by the current thread, its value will be
* reinitialized by invoking its {@link #initialValue} method,
* unless its value is {@linkplain #set set} by the current thread
* in the interim. This may result in multiple invocations of the
* {@code initialValue} method in the current thread.
*
* @since 1.5
*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
remove()
方法获取当前线程的副本变量,来指定移除对应的值;- 此方法为 1.5 引入,旨在协助
jvm
进行有效的快速gc
; - 假如不移除
ThreadLocal
副本变量 另外ThreadLocalMap
的key
为弱引用