您好,登錄后才能下訂單哦!
這篇文章主要介紹“Java線程變量ThreadLocal源碼分析”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Java線程變量ThreadLocal源碼分析”文章能幫助大家解決問題。
1.ThreadLocal 線程變量,和當前線程綁定的,只保存當前線程的變量,對于其他線程是隔離的,是訪問不到里面的數據的。
2.在Looper中使用到了ThreadLocal,創建了一個Looper是保存到了ThreadLocal中。
//這里用到了泛型,ThreadLocal中只保存Looper對象。 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { //保證Looper只被創建一次。 throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
看下sThreadLocal.set()方法是如何保存數據的。
先拿到當前線程,然后在拿到該線程的ThreadLocalMap成員變量,然后保存到這個map中,
key就是創建的ThreadLocal對象,value就是傳進來的value。
public void set(T value) { //拿到當前線程 Thread t = Thread.currentThread(); //得到一個map ThreadLocalMap map = getMap(t); if (map != null){ // 這個map是以當前對象為key的,這個this就是 ThreadLocal的實例 sThreadLocal map.set(this, value); }else{ createMap(t, value); } }
//getMap 是從Thread中拿到了一個threadLocals變量,是ThreadLocal.ThreadLocalMap 的實例。 //保存的數據也是存在了這個map中,這也就是為什么ThreadLocal是和線程綁定的,對其他線程來說是隔離的原因所在。 ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
1)保存數據,如果map不為空的情況。走上面if判斷的第一個分支。這個存儲方式和HashMap類似
private void set(ThreadLocal<?> key, Object value) { Entry[] tab = table; int len = tab.length; // 計算出key在集合中的索引,index int i = key.threadLocalHashCode & (len-1); //開始遍歷整個數組, //取出索引為i的Entry,如果不為空,取出下一個,進行遍歷 for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get(); //如果取出的k和傳進來的key一致,則把新的值存起來。 if (k == key) { e.value = value; return; } //直到取出最有一個,k==null則進行存儲。 if (k == null) { replaceStaleEntry(key, value, i); return; } } //如果索引i的位置,沒有Entry,則把傳進來的key和value保存在這個位置。 tab[i] = new Entry(key, value); int sz = ++size; //如果大于閾值了,則進行擴容 if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
2)保存數據,如果map為空的情況。則創建ThreadLocalMap,并賦值給當前線程t。
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { //創建一個大小為16的數組 table = new Entry[INITIAL_CAPACITY]; //計算得到的i是數組的角標。可以參考hashMap源碼分析 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); //賦值,保存數據 table[i] = new Entry(firstKey, firstValue); size = 1; //擴容的閾值 setThreshold(INITIAL_CAPACITY); }
3.再看看ThreadLocal是如何取值的。
也是先拿到當前線程t,然后通過t拿到他的成員變量ThreadLocalMap。
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); //如果map不為空,則從map中取值 if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } //如果map為空 return setInitialValue(); }
1)如果map不為空則從map中取值。
//如果map不為空 private Entry getEntry(ThreadLocal<?> key) { //拿到key對應的索引 int i = key.threadLocalHashCode & (table.length - 1); //從數組中拿到Entry Entry e = table[i]; if (e != null && e.get() == key){如果key一樣直接返回 return e; }else{//如果不一致則開始遍歷 return getEntryAfterMiss(key, i, e); } }
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { Entry[] tab = table; int len = tab.length; while (e != null) { ThreadLocal<?> k = e.get(); if (k == key) return e; if (k == null) expungeStaleEntry(i); else i = nextIndex(i, len); e = tab[i]; } return null; }
2)如果在get時,得到的map是空的,則這個時候需要初始化
//如果map為空,則調用這個方法,initialValue由用戶去實現。 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; }
//下面是Choreographer中的例子: private static final ThreadLocal<Choreographer> sThreadInstance = new ThreadLocal<Choreographer>() { @Override protected Choreographer initialValue() { Looper looper = Looper.myLooper(); if (looper == null) { throw new IllegalStateException("The current thread must have a looper!"); } Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP); if (looper == Looper.getMainLooper()) { mMainInstance = choreographer; } return choreographer; } };
總結ThreadLocal是通過 ThreadLocalMap 進行數據的存儲的。而這個ThreadLocalMap對象是通過
獲取到當前線程,并從當前線程中拿到的。所以ThreadLocalMap只保存本線程的數據,做到了線程隔離。
ThreadLocalMap存數據的key是ThreadLocal對象本身。 map.set(this, value);
如果想要給ThreadLocalMap中存更多的數據,則需要創建多個對象。
關于“Java線程變量ThreadLocal源碼分析”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。