網(wǎng)站上的地圖怎么做小程序開發(fā)教程全集免費(fèi)
文章目錄
- example
- set(T value)
- createMap(t, value);
- set(ThreadLocal<?> key, Object value)
- ThreadLocalMap和Thread的關(guān)系
- 全貌
ThreadLocal是個(gè)很重要的多線程類,里面數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì)很有意思,很巧妙。但是我們平時(shí)使用它的時(shí)候常常容易對它的使用感到迷惑,因?yàn)樗渌腁PI很不一樣,使用很不一樣,設(shè)計(jì)也很不一樣。
但是不用擔(dān)心,這篇文章將從源碼出發(fā),一步步深入剖析ThreadLocal內(nèi)部構(gòu)造,理清楚它的來龍去脈。
example
還是從一個(gè)使用用例出發(fā):
public class ThreadLocalExample {// 聲明一個(gè) ThreadLocal 變量private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 在主線程中設(shè)置變量值threadLocal.set(10);// 創(chuàng)建并啟動(dòng)一個(gè)新線程Thread thread = new Thread(() -> {// 在新線程中獲取變量值System.out.println("ThreadLocal value in new thread: " + threadLocal.get());});thread.start();// 在主線程中獲取變量值System.out.println("ThreadLocal value in main thread: " + threadLocal.get());}
}
打印結(jié)果:
ThreadLocal value in main thread: 10
ThreadLocal value in new thread: null
可以看出同一個(gè)threadLocal 對象,在不同的線程里面調(diào)用get方法,獲取的是不一樣的結(jié)果!也就是說,threadLocal 對象存儲(chǔ)了不同線程的私有變量。
現(xiàn)在可能我們還是覺得云里霧里,那現(xiàn)在我們就從源碼出發(fā),來一步步進(jìn)行分析。
從threadLocal.set(10);方法進(jìn)去:
set(T value)
/*** 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);elsecreateMap(t, value);}
創(chuàng)建map或者設(shè)置key&value。
createMap(t, value);
先來看看假如map==null
它怎么做:
/*** 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);}
這里把new出來的ThreadLocalMap賦值給了t.threadLocals
,t是個(gè)線程。
/*** 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);}
這里是ThreadLocalMap的構(gòu)造方法,可以看出ThreadLocal作為key,傳進(jìn)來的參數(shù)作為value。
set(ThreadLocal<?> key, Object value)
現(xiàn)在回退一下,看看map.set(this, value);
做了什么:
/*** 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();}
這里有必要知道的是該方法位于ThreadLocalMap類里面:
看下Entry 的實(shí)現(xiàn)代碼:
/*** 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;}}
Entry 是個(gè)弱引用的子類。設(shè)計(jì)為弱引用,說明它跟內(nèi)存泄漏有關(guān)。這里先不深入探討。
到這里我們可以得知set方法執(zhí)行的時(shí)候以ThreadLocal對象作為key,以value入?yún)⒆鳛関alue,傳到了ThreadLocal的 Entry[] 里面,設(shè)置的時(shí)候根據(jù)threadLocal對象的hash值來確定其在ThreadLocalMap中的位置。
ThreadLocalMap和Thread的關(guān)系
還記得前面的這行代碼嗎?
t.threadLocals = new ThreadLocalMap(this, firstValue);
它createMap(t, value);
里面,來看看ThreadLocalMap和Thread的關(guān)系:
java/lang/Thread.java
/* ThreadLocal values pertaining to this thread. This map is maintained* by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;
也就是說ThreadLocalMap 其實(shí)是屬于線程的成員變量。
全貌
其實(shí)到這里,我們已經(jīng)有能力知道整體的數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì)了,下面我們通過前面給出的example代碼,通過畫圖的方式把它們之間關(guān)系全貌繪制出來:
ThreadLocalMap里面是Entry數(shù)組,那么其它Entry元素怎么用呢?
這是個(gè)好問題,我們迭代下前面的example:
class ThreadLocalExample {// 聲明一個(gè) ThreadLocal 變量private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();private static ThreadLocal<String> threadLocal2 = new ThreadLocal<>();public static void main(String[] args) {// 在主線程中設(shè)置變量值threadLocal.set(10);threadLocal2.set("hello");// 創(chuàng)建并啟動(dòng)一個(gè)新線程Thread thread = new Thread(() -> {// 在新線程中獲取變量值System.out.println("ThreadLocal value in new thread: " + threadLocal.get());System.out.println("ThreadLocal2 value in new thread: " + threadLocal2.get());});thread.start();// 在主線程中獲取變量值System.out.println("ThreadLocal value in main thread: " + threadLocal.get());System.out.println("ThreadLocal2 value in main thread: " + threadLocal2.get());}
}
運(yùn)行結(jié)果:
ThreadLocal value in main thread: 10
ThreadLocal value in new thread: null
ThreadLocal2 value in main thread: hello
ThreadLocal2 value in new thread: null
再來一個(gè)圖:
一圖勝千言,到這里我們應(yīng)該對ThreadLocal這個(gè)線程類的整體有個(gè)清晰的把握了。
enjoy it。