ThreadLocal
對象是線程的局部變量,每個線程都能在其中儲存隻屬于自己的内容。對于同一個static ThreadLocal,不同線程隻能從中get,set,remove自己的變量,而不會影響其他線程的變量。
結構體系大概如下:

ThreadLocal如何保證這些變量隻被目前線程所通路的呢?
ThreadLocal.set
首先看一下ThreadLocal的set():
public class ThreadLocal<T> {
/* 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;
/**
* 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時,首先擷取目前線程對象,然後通過getMap擷取線程的ThreadLocalMap,并将值設定ThreadLocalMap中,ThreadLocalMap可以了解為一個Map。map.set()如下:
static class ThreadLocalMap {
/**
* 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;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
}
方法中循環了map中的元素,對map中的key和入參key比較,如果相同則賦新值,如果map中key存在null值則替換,否則重新生成一個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);
}
createMap中構造了ThreadLocalMap:
/**
* Construct a new map initially containing (firstKey, firstValue).
* 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) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
在這裡,為Thread的threadLocals指派。
ThreadLocal.get
而設定到ThreadLocal中的資料,實質上是寫入了Thread的threadLocals屬性。其中key為ThreadLocal目前對象,value就是我們需要的值。Thread的threadLocals儲存了目前自己所線上程的所有“局部變量”,也就是一個ThreadLocal變量的集合。
在進行get操作時,自然就是将這個Map中的資料拿出來:
/**
* 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對象,然後,将自己傳入作為getMap的參數,而getMap傳回的則是目前線程的threadLocals屬性:
/**
* 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;
}
即目前線程的threadLocals屬性,也就是說,ThreadLocal類的getMap方法傳回的是Thread類的threadLocals屬性:
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;
是以,threadLocals是維護在Thread内部的,這意味着隻要線程不退出,對象的引用就一直存在。
當線程退出時會進行一些清理工作,其中包括對ThreadLocalMap的清理:
/**
* This method is called by the system to give a Thread
* a chance to clean up before it actually exits.
*/
private void exit() {
if (group != null) {
group.threadTerminated(this);
group = null;
}
/* Aggressively null out all reference fields: see bug 4006245 */
target = null;
/* Speed the release of some of these resources */
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}
如果使用線程池的話,線程未必會退出,此時将一些比較大的對象放入ThreadLocal中(實際儲存線上程持有的threadLocals Map中),可能會導緻記憶體洩漏的問題。這時可以使用ThreadLocal.remove()進行移除:
/**
* 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);
}
執行個體示範
public class Test {
static ThreadLocal<String> threadLocal = new ThreadLocal<>();
static ThreadLocal<String> anotherThreadLocal = new ThreadLocal<>();
static AtomicInteger atomicInteger = new AtomicInteger(0);
static class ThreadDemo implements Runnable {
@Override
public void run() {
if (threadLocal.get() == null) {
threadLocal.set("線程:" + atomicInteger.addAndGet(1));
}
if (anotherThreadLocal.get() == null) {
anotherThreadLocal.set("anotherThreadLocal:" + atomicInteger.get());
}
System.out.println(threadLocal.get());
System.out.println(anotherThreadLocal.get());
}
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executorService.execute(new ThreadDemo());
}
}
}
表明,一個ThreadLocal可以供多個線程共享,但是每個線程隻能拿到屬于自己的那一份存儲。每一個線程的threadLocals的大小可以為多個,每一個ThreadLocal對象都可以會作為該map的key存在。