Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

lishanquan/ThreadLocal

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

4 Commits

Repository files navigation

深入解析ThreadLocal

ThreadLocal类提供的几个方法:

public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }

get()方法用来获取ThreadLocal当前线程中保存的变量副本;
set()用来设置当前线程中变量的副本;
remove()用来移除当前线程中变量的副本;
initialValue()是一个protected方法,一般是用来在使用时进行重写,它是一个延迟加载的方法。
首先来看下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 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();
 }

第一句话是取得当前线程,然后通过getMap(t)方法获取一个ThreadLocalMap类型的map,接着获取到该map中的<key,value>键值对,注意这里获取键值对传进去的是this,而不是当前线程t。(?)
如果获取成功,则返回value;
如果map为空,则调用setInitialValue()方法返回值。
我们来根据上面的每一句来仔细分析:
首先看getMap()方法中做了什么:

 /**
 * 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;
 }

在getMap(t)方法中,是调用当前线程t,返回当前线程t中的一个成员变量threadlocals,注意:这个threadlocals是Thread中的成员变量,而不是ThreadLocal中。
那么我们继续看下Thread类中的成员变量threadlocals是什么:

 /* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
 ThreadLocal.ThreadLocalMap threadLocals = null;

实际上就是一个ThreadLocalMap,这个类型是ThreadLocal当中的一个静态内部类,我们看下ThreadLocalMap的实现:

 /**
 * 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;
 }
 }

可以看到ThreadLocalMap中的Entry继承了WeakReference,并且使用ThreadLocal作为键值。
然后再继续看setInitialValue()方法的具体实现:

 /**
 * 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;
 }

如果map不为空,就设置键值对;为空,再创建Map,跟下去看createMap()的实现:

 /**
 * 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是如何为每个线程创建变量的副本的:
首先,每个线程Thread内部有一个Thread.ThreadLocalMap类型的成员变量threadlocals,这个threadlocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。
初始时,在Thread里面,threadlocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadlocals进行初始化,并且以当前ThreadLocal为键值,以ThreadLocal要保存的副本变量为value,存到threadlocals中。
然后,在当前线程里面,如果要使用副本变量,就可以通过get方法在threadlocals里面查找。

总结
1. 通过ThreadLocal创建的副本是存储在每个线程自己的threadlocals中的;
2. 为何threadlocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可以有多个ThreadLocal变量,例如ThreadLocal,ThreadLocal;
3. 在进行get之前,必须先set,否则会报空指针异常;如果想在调用get之前不调用set就能正常访问的话,必须重写initialValue()方法。

About

ThreadLocal study

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

AltStyle によって変換されたページ (->オリジナル) /