ThreadLocal 如何实现为每个线程提供一个独立的变量副本
一、核心
当使用ThreadLocal维护变量的时候 为每一个使用该变量的线程提供一个独立的变量副本
二、具体实现(源码)
当我们获取需要的元素时,通过的它的 get() 方法,那么就以这里为起点开始分析吧,源码如下
/**
* 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 内部类对象,源码声明为: static class ThreadLocalMap
//本质上就是一个map
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(); ...............⑥
}
从 ①开始吧:
通过 ①判断,map 不等于 null 时,就执行②,获取实体,它的源码如下:
/**
* Get the entry associated with key. This method
* itself handles only the fast path: a direct hit of existing
* key. It otherwise relays to getEntryAfterMiss. This is
* designed to maximize performance for direct hits, in part
* by making this method readily inlinable.
*
* @param key the thread local object
* @return the entry associated with key, or null if no such
*/
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
然后③判断实体是不是等于空,非空执行④⑤,获取值并返回值
**如果实体等于 null 或者在.①时 map为null**,都会执行 setInitialValue() 方法,源码如下,
private T setInitialValue() {
T value = initialValue(); ...............⑦
Thread t = Thread.currentThread(); ...............⑧
ThreadLocalMap map = getMap(t); ...............⑨
if (map != null) ...............⑩
map.set(this, value);
else ...............十一,输入法只能到10
createMap(t, value);
return value; ...............十二
}
而 ⑦的源码如下:
/**
* 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}.
*
* <p>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;
}
可以看到,这是一个 protected 方法,也就是说,你可以重写这个方法,并确定你的返回值,比如:
ThreadLocal<T> threadLocal = new ThreadLocal<T>(){
@Override
protected T initialValue() {
return t;
}
};
⑧ 获取当前线程对象
⑨ 根据当前线程对象,获取 ThreadLocalMap,源码如下:
/**
* 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;
}
也就是说,每个线程都会有各自的 ThreadLocalMap,所以,对于要维护的变量而言,它们都会存放在各自线程的 ThreadLocalMap 中,
⑩ map 不为空,设置值
map 为空, 则创建一个 map,源码如下:
/**
* 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);
}
我想,源码看到这里,思路也大致明白了。至于 ThreadLocalMap 和 t.threadLocals 就不去看了,感觉没啥好看的,就是一个map,有兴趣大家自己看吧