共计 6392 个字符,预计需要花费 16 分钟才能阅读完成。
ThreadLocal 是一个线程外部的存储类,能够在指定线程内存储数据,数据存储当前,只有指定线程能够失去存储数据,ThreadLocal 提供了线程内存储变量的能力,这些变量不同之处在于每一个线程读取的变量是对应的相互独立的。通过 get 和 set 办法就能够失去以后线程对应的值,ThreadLocal 不是用来解决共享对象的多线程拜访问题的,通过 ThreadLocal 的 set()办法设置到线程的 ThreadLocal.ThreadLocalMap 里的是是线程本人要存储的对象,其余线程不须要去拜访,也是拜访不到的。各个线程中的 ThreadLocal.ThreadLocalMap 以及 ThreadLocal.ThreadLocal 中的值都是不同的对象。
1、每个线程都有一个本人的 ThreadLocal.ThreadLocalMap 对象
2、每一个 ThreadLocal 对象都有一个循环计数器
3、ThreadLocal.get()取值,就是依据以后的线程,获取线程中本人的 ThreadLocal.ThreadLocalMap,而后在这个 Map 中依据第二点中循环计数器获得一个特定 value 值
set
1、先对 ThreadLocal 外面的 threadLocalHashCode 取模获取到一个 table 中的地位
2、这个地位上如果有数据,获取这个地位上的 ThreadLocal
(1)判断一下地位上的 ThreadLocal 和我自身这个 ThreadLocal 是不是一个 ThreadLocal,是的话数据就笼罩,返回
(2)不是同一个 ThreadLocal,再判断一下地位上的 ThreadLocal 是是不是空的,这个解释一下。Entry 是 ThreadLocal 弱援用,”static class Entry extends WeakReference<ThreadLocal>”,有可能这个 ThreadLocal 被垃圾回收了,这时候把新设置的 value 替换到以后地位上,返回
(3)下面都没有返回,给模加 1,看看模加 1 后的 table 地位上是不是空的,是空的再加 1,判断地位上是不是空的 … 始终到找到一个 table 上的地位不是空的为止,往这外面塞一个 value。换句话说,当 table 的地位上有数据的时候,ThreadLocal 采取的是方法是找最近的一个空的地位设置数据。
3、如果没有数据,实例化一个新的 ThreadLocalMap,并赋值给线程的成员变量 threadLocals
/**
* Sets the current thread's copy of this thread-local variable 将此线程局部变量的以后线程正本设置为指定值。大多数子类将不须要重写此办法,而仅依附 {@link #initialValue} 办法来设置线程局部变量的值。* 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 @param value 要存储在此本地线程的以后线程正本中的值。* 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);
}
/**
* 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);
}
/**
* Set the value associated with key.
*
* @param key the thread local object
* @param value the value to be set
*/
private void set(ThreadLocal<?> key, Object value) {// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
// 对 ThreadLocal 外面的 threadLocalHashCode 取模获取到一个 table 中的地位
int i = key.threadLocalHashCode & (len-1);
// 这个地位上如果有数据,获取这个地位上的 ThreadLocal
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();
// 判断一下地位上的 ThreadLocal 和我自身这个 ThreadLocal 是不是一个 ThreadLocal,是的话数据就笼罩,返回
if (k == key) {
e.value = value;
return;
}
// 不是同一个 ThreadLocal,再判断一下地位上的 ThreadLocal 是是不是空的,Entry 是 ThreadLocal 弱援用,"static class Entry extends WeakReference<ThreadLocal>",有可能这个 ThreadLocal 被垃圾回收了
if (k == null) {
// 设置新的 value 替换到以后地位上,返回
replaceStaleEntry(key, value, i);
return;
}
}
// 下面都没有返回,给模加 1,看看模加 1 后的 table 地位上是不是空的,是空的再加 1,判断地位上是不是空的... 始终到找到一个 table 上的地位不是空的为止,往这外面塞一个 value。换句话说,当 table 的地位上有数据的时候,ThreadLocal 采取的 // 是方法是找最近的一个空的地位设置数据
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();}
get
1、获取以后线程
2、尝试去以后线程中拿它的 ThreadLocal.ThreadLocalMap
3、以后线程中判断是否有 ThreadLocal.ThreadLocalMap
(1)有就尝试依据以后 ThreadLocal 的 threadLocalHashCode 取模去 table 中取值,有就返回,没有就给模加 1 持续找,这和设置的算法是一样的
(2)没有就调用 set 办法给以后线程 ThreadLocal.ThreadLocalMap 设置一个初始值
/**
* Returns the value in the current thread's copy of this 返回此线程局部变量的以后线程正本中的值。如果该变量没有以后线程的值,则首先将其初始化为调用 {@link #initialValue} 办法返回的值。* 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();
// 尝试去以后线程中拿它的 ThreadLocal.ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 判断以后线程中判断是否有 ThreadLocal.ThreadLocalMap
if (map != null) {
// 有就尝试依据以后 ThreadLocal 的 threadLocalHashCode 取模去 table 中取值,有就返回,没有就给模加 1 持续找
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// 没有就调用 set 办法给以后线程 ThreadLocal.ThreadLocalMap 设置一个初始值
return setInitialValue();}
/**
* 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) {
// 依据以后 ThreadLocal 的 threadLocalHashCode 取模去 table 中取值
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
// 有就返回,没有就给模加 1 持续找
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
remove
/**
* Removes the current thread's value for this thread-local 删除此线程局部变量的以后线程值。如果此线程局部变量随后被以后线程 {@linkplain #get read} 调用,则其值将通过调用其 {@link #initialValue} 办法来从新初始化,除非
* variable. If this thread-local variable is subsequently 以后值是 {@linkplain #set set} 在此期间穿线。这可能会导致在以后线程中屡次调用{@code initialValue} 办法。* {@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() {
// 获得以后线程的 ThreadLocal.ThreadLocalMap,如果有 ThreadLocal.ThreadLocalMap,找到对应的 Entry,移除掉
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocalMap
实例化 ThreadLocalMap 时创立了一个长度为 16 的 Entry 数组。通过 hashCode 与 length 位运算确定出一个索引值 i,这个 i 就是被存储在 table 数组中的地位,每个线程 Thread 都持有一个 Entry 型的数组 table,而所有的读取过程都是通过操作这个数组 table 实现的。
/**
* Construct a new map initially containing (firstKey, firstValue). 结构一个最后蕴含(firstKey,firstValue)的 Map。ThreadLocalMaps 是提早结构的,因而只有在至多有一个条目要搁置时才创立一个。* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
// 长度为 16 的 Entry 数组
table = new Entry[INITIAL_CAPACITY];
// 被存储在 table 数组中的地位
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
// 设置长度
setThreshold(INITIAL_CAPACITY);
}
/**
* Set the resize threshold to maintain at worst a 2/3 load factor. 从新设置长度
*/
private void setThreshold(int len) {threshold = len * 2 / 3;}
论断
- ThreadLocal 不须要 key,因为线程外面本人的 ThreadLocal.ThreadLocalMap 不是通过链表法实现的,而是通过开地址法实现的
- 每次 set 的时候往线程外面的 ThreadLocal.ThreadLocalMap 中的 table 数组某一个地位塞一个值,这个地位由 ThreadLocal 中的 threadLocaltHashCode 取模失去,如果地位上有数据了,就往后找一个没有数据的地位
- 每次 get 的时候也一样,依据 ThreadLocal 中的 threadLocalHashCode 取模,获得线程中的 ThreadLocal.ThreadLocalMap 中的 table 的一个地位,看一下有没有数据,没有就往下一个地位找
- 既然 ThreadLocal 没有 key,那么一个 ThreadLocal 只能塞一种特定数据。如果想要往线程外面的 ThreadLocal.ThreadLocalMap 里的 table 不同地位塞数据,比方说想塞三种 String、一个 Integer、两个 Double、一个 Date,请定义多个 ThreadLocal,ThreadLocal 反对泛型 ”public class ThreadLocal<T>”