了解Java的Synchronized機制的大家想必都了解過,這個鎖有多么的強大和美妙,它就像哪吒有三頭六臂,手上拽著一堆法寶,
它可以用在類上,可以用在方法上面,甚至可以用在代碼塊上面,一個執行緒需要獲取被Synchronized修飾過的方法或者物件,通常需要阻塞等待一下,
就好像大家都需要拿著電影票,挨個排好隊,依次通過檢票員的驗證,才可以進入影廳觀看電影,
當你覺得用得很順手的時候,繼續深挖原理,往往會給你當頭一盆冷水,

Synchronized機制的使用:
1. 帶來了不小的時間消耗,加鎖、解鎖都需要額外操作, 我曾經寫過一篇關于python的的鎖機制(寫得比較簡單,有興趣可以出門左轉),大致描述了一下python的鎖是怎么獲得和釋放的, 語言之間也是有共通之處的,雖然Java對我們隱藏了Synchronized的實作邏輯,但道理還是差不多的, 2、帶來了不少的系統開銷,互斥同步對性能最大的影響是阻塞的實作,因為阻塞涉及到的掛起執行緒和恢復執行緒的操作都需要轉入內核態中完成 用戶態與內核態的切換的性能代價是比較大的,這里就不展開描述了,我自己理解也不是很深刻 話雖這么說,Synchronized有如此強大的機制,當然會有它的一席用武之地,在實際專案中,好比如,獲取Jedis實體的時候會用到,redis一般也就是存盤一下用戶的session, 對并發的要求不是非常嚴格,我們都知道執行緒執行任務都是在一定的CPU時間片里面進行的,如果大家都阻塞等待了,那未免也太對不起現代的多核CPU了吧,
好了,說了這么多,接下來才是引入今天的主角,ThreadLocal,
為什么說她是各掃門前雪呢?道理很簡單,ThreadLocal的實作機制就是在當前執行緒內部存盤自己的一份資料,這份資料不受其他執行緒的干擾,
有些文章會說是在復制了一份副本存盤在執行緒內部,
ThreadLocal主要有兩個方法,get() 和 set()
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(); }
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
從代碼不難看出,ThreadLocal內部實際上是定義了一個叫做ThreadLocalMap 的 HashMap,用于存盤當前執行緒的資料
再往下一步,原始碼對TreadLocalMap有一個比較詳盡的描述:
/**
* 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.
*/
意思大概是,由于Map使用WeakReference弱參考物件作為key,因為沒有使用WeakRefrence的弱參考佇列(即key具有唯一性)
WeakRefrence這個東東也是一個很奇妙的東西,有興趣可以在園子里面逛逛,看看相關介紹啦,我自己也是一步步追溯下來理解的,這里就不展開了,
所以只有當Map的資料非常大,已經超出規定的記憶體空間的時候才會被GC掉,這樣保障了單個復雜操作程序,執行緒中ThreadLocal資料的穩定,
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. */ // 只有當key是null的時候,才會被定義為陳舊的資料被移除
// static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
// ThreadLocalMap的構造
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實際上是一個陣列結構,用于存盤ThreadLocal資料,ThreadLocal內部定義了一個水位線,當記憶體大于這個水位線的時候,會先把陳舊的資料線GC掉;
如果GC的效果并不理想,會把水位線值升高,增大記憶體,
這樣做的好處當然是為了避免頻繁的GC和保持資料的穩定性啦,
/** * Re-pack and/or re-size the table. First scan the entire * table removing stale entries. If this doesn't sufficiently * shrink the size of the table, double the table size. */ private void rehash() {
// 先清理陳舊資料 expungeStaleEntries(); // Use lower threshold for doubling to avoid hysteresis if (size >= threshold - threshold / 4) resize(); }
講了這么一些內容,總結一下:
1. ThreadLocal實際上是用在執行緒內部存盤資料,避免Thread爭奪資源導致產生了臟的資料,
2. 其內部運用水位線的機制來控制什么時候進行記憶體的GC,保障資料穩定性的同時也減小系統開銷
至此,ThreadLocal為什么自掃門前雪,大概有一個比較好的解釋了,那就是我管我自己的事情,跟你其他人(執行緒)一點關系都沒有!
你喜歡堆雪人,我喜歡打雪仗,大家互不干擾,各干各的,
那么ThreadLocal在實際應用中會怎么用呢?我會在下一篇:Spring的資料庫的主從配置 里面進行介紹
世界上有些東西就是這么美妙,當你一層層剝開它的時候,會忍不住說,哇塞,太棒了吧,
獻上楊宗緯的《洋蔥》:
如果你愿意一層一層
一層地剝開我的心
你會鼻酸
你會流淚
只要你能
聽到我
看到我的全心全意
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/500106.html
標籤:其他
