一、概念
首先,ThreadLocal並不是一個Thread,這個類提供了線程局部變量,這些變量不同於它們的普通對應物,因為訪問某個變量的每個線程都有自己的局部變量,它獨立於變量的初始化副本。
二、基本原理
ThreadLocal是如何做到為每一線程維護變量的副本的呢?下面通過源碼(jdk1.7版本)來闡述ThreadLocal的基本原理。
在具體分析之前,先做幾點說明:
1:在TreadLocal中有一個靜態內部類ThreadLocalMap
static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } } ... }
2:ThreadLocal中又定義一個鍵值對Entry,它用ThreadLocal作為鍵值。我們看到在Thread類中有一個ThreadLocalMap的類型的變量叫做threadLocals。
下面具體分析一下ThreadLocal的兩個關鍵函數get()和set():
1、get()方法
public T get()
{ Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); }
在get函數中,首先獲取到當前的線程t,再根據t獲取ThreadLocalMap。下面試getMap()函數:
ThreadLocalMap getMap(Thread t)
{ return t.threadLocals; }
可以看到,該函數返回就是我們上面提到的每個線程都有的ThreadLocalMap類型變量threadLocals。
如果map不為空,則根據map.getEntry(this)獲取Entry鍵值對。注意:這里的this指的是當前的ThreadLocal對象,一個Thread可能對應不止一個ThreadLocal,想要知道具體是Thread對應的哪個ThreadLocal,就要在Thread中維護一個ThreadLocalMap,以ThreadLocal為鍵,就可以找到Thread在某個ThreadLocal里對應的本地數據。獲取到Entry后,我們就可以拿到保存在Entry里面的value值了。
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); }
如果map為空,則調用setInitialValue()函數進行初始化。並返回initialValue函數返回的值,不覆寫initialValue的情況下,返回的是null。
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; }
2、set()方法
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函數同樣是先獲取ThreadLocalMap類型的變量map。
如果map不為空,則:
private void set(ThreadLocal key, Object value) { 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為空,則
void createMap(Thread t, T firstValue)
{ t.threadLocals = new ThreadLocalMap(this, firstValue); }
三、總結
ThreadLocal是通過下面的方式來實現為每一個線程維護變量的副本的:
在ThreadLocal類中定義了一個ThreadLocalMap,每一個Thread都有一個ThreadLocalMap類型的變量threadLocals,就是用threadLocals來存儲每一個線程的變量副本,threadLocals內部有一個Entry數組,我們根據鍵值線程對象,來找到對應線程的變量副本。