三. 线程管理之ThreadLocal
不忘初心 砥礪前行, Tomorrow Is Another Day !
相關(guān)文章
- 一. 線程管理之Thread基礎(chǔ)
- 二. 線程管理之線程池
- 三. 線程管理之ThreadLocal
- 四. 線程管理之Android中的多線程
本文概要:
在Android系統(tǒng)源碼中,多處用到了Threadlocal,如最熟悉的Handler中的Looper,其次還有屬性動畫中AnimationHandler、ActivityThread、AMS都有涉及到.接下來一起來認(rèn)識與了解它.
一. 認(rèn)識ThreadLocal
概念:線程內(nèi)部的數(shù)據(jù)存儲類,可以實現(xiàn)在不同線程具有不同數(shù)據(jù)副本.它的作用域僅限于當(dāng)前當(dāng)前線程,線程之間互不干擾.
基本使用
這里定義了一個ThreadLocal用來存儲Integer類型的數(shù)據(jù),設(shè)置了默認(rèn)值為999.分別在MainThread,SubThreadA對ThreadLocal的值進(jìn)行設(shè)置,SubThreadB未進(jìn)行設(shè)置值.
示例源碼
public class ThreadLocalActivity extends AppCompatActivity {private static final String TAG = "ThreadLocalActivity";private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){@Overrideprotected Integer initialValue() {return 999;}};public static void startActivity(Context pkgContext) {Intent intent = new Intent(pkgContext, ThreadLocalActivity.class);pkgContext.startActivity(intent);}@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_threadlocal);threadLocal.set(100);new SubThreadA("SubThreadA").start();new SubThreadB("SubThreadB").start();System.out.println("MainThread:" + threadLocal.get());}class SubThreadA extends Thread {public SubThreadA(String name) {super(name);}@Overridepublic void run() {threadLocal.set(110);int value = threadLocal.get();System.out.println("SubThreadA:" + value);}}class SubThreadB extends Thread {public SubThreadB(String name) {super(name);}@Overridepublic void run() {int value = threadLocal.get();System.out.println("SubThreadB:" + value);}}}//調(diào)用輸出 I: SubThreadA:110 I: MainThread:100 I: SubThreadB:999復(fù)制代碼最后通過調(diào)用輸出結(jié)果,可以清晰的看到雖然操作的是同一個ThreadLocal對象,但是在三個不同線程存儲的值是互不影響的.回過頭再對照看概念性內(nèi)容就清晰明了許多.另外在SubThreadB因為沒有設(shè)置值,所以獲得的是我們初始化設(shè)置的默認(rèn)值999.
二. 了解ThreadLocal的實現(xiàn)原理
認(rèn)識了它基本使用,接著一起來了解ThreadLocal的基本原理,之所以用了解,因為筆者自己對ThreadLocal的理解有限且實際開發(fā)中運用較少.這里就簡單的看下原理.
2.1 當(dāng)調(diào)用Set方法進(jìn)行存儲值時.
對應(yīng)源碼
public void set(T value) {Thread t = Thread.currentThread();//獲取當(dāng)前線程的ThreadLocalMapThreadLocalMap map = getMap(t);if (map != null)//不為空,直接存儲值map.set(this, value);else//為空,創(chuàng)建一個ThreadLocalMap并存儲值.createMap(t, value);} 復(fù)制代碼從上面源碼可以看出,我們設(shè)置的值,是存儲在ThreadLocalMap下的.當(dāng)?shù)谝淮未鎯r,先會createMap初始化一個ThreadLocalMap.接著看如何進(jìn)行初始化的.
對應(yīng)源碼
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue); }ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {table = new Entry[INITIAL_CAPACITY];int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);//將新值存儲在table數(shù)組中,該數(shù)組類型是Entry,擴(kuò)展了弱引用封裝了ThreadLocal與value.table[i] = new Entry(firstKey, firstValue);size = 1;setThreshold(INITIAL_CAPACITY); } 復(fù)制代碼通過以上源碼,可以很清楚的發(fā)現(xiàn)最終我們設(shè)置的值,是存儲在一個Entry類型的table數(shù)組中的,這個Entry封裝了ThreadLocal與Value.
接著,如果不是第一次set值,那么會進(jìn)入ThreadLocalMap的set方法,我們繼續(xù)往下看.
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;}}//將值存儲在table數(shù)組中tab[i] = new Entry(key, value);int sz = ++size;if (!cleanSomeSlots(i, sz) && sz >= threshold)rehash();} 復(fù)制代碼這里不分析具體算法,從上面可以發(fā)現(xiàn)最終也是保存在table數(shù)組中.
2.2 當(dāng)調(diào)用Get方法獲取存儲值時.
對應(yīng)源碼
public T get() {Thread t = Thread.currentThread();//同樣獲取當(dāng)前線程的ThreadLocalMapThreadLocalMap map = getMap(t);if (map != null) {//不為空,獲取對應(yīng)值ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}//為空,返回設(shè)置的默認(rèn)值return setInitialValue(); } 復(fù)制代碼通過以上源碼,get方法簡單來說就是從當(dāng)前線程的ThreadLocalMap對象中獲取存儲的值.
通過SET與GET源碼簡單分析,我們知道它們所操作的對象都是當(dāng)前線程的ThreadLolocalMap對象的table數(shù)組,因此在不同線程中訪問同一個ThreadLocal的set和get方法,它們對ThreadLocal所做的讀寫操作僅限于各自線程的內(nèi)部.
本文小結(jié)
最后我們對ThreadLocal的實現(xiàn)流程做一個小結(jié).
- 該table數(shù)組是Entry類型,封裝了ThreadLocal與Value.
最后奉上一個極簡版的ThreadLocal的工作流程圖.
ThreadLocal工作流程極簡圖
由于本人技術(shù)有限,如有錯誤的地方,麻煩大家給我提出來,本人不勝感激,大家一起學(xué)習(xí)進(jìn)步.
參考鏈接:
- www.cnblogs.com/whoislcj/p/…
總結(jié)
以上是生活随笔為你收集整理的三. 线程管理之ThreadLocal的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用注解装配Bean
- 下一篇: 1-AII--BroadcastRece