不忘初心 砥礪前行, Tomorrow Is Another Day !html
本文概要:算法
在Android系統源碼中,多處用到了Threadlocal,如最熟悉的Handler中的Looper,其次還有屬性動畫中AnimationHandler、ActivityThread、AMS都有涉及到.接下來一塊兒來認識與瞭解它.數組
概念:線程內部的數據存儲類,能夠實如今不一樣線程具備不一樣數據副本.它的做用域僅限於當前當前線程,線程之間互不干擾.bash
這裏定義了一個ThreadLocal用來存儲Integer類型的數據,設置了默認值爲999.分別在MainThread,SubThreadA對ThreadLocal的值進行設置,SubThreadB未進行設置值.多線程
示例源碼ide
public class ThreadLocalActivity extends AppCompatActivity {
private static final String TAG = "ThreadLocalActivity";
private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 999;
}
};
public static void startActivity(Context pkgContext) {
Intent intent = new Intent(pkgContext, ThreadLocalActivity.class);
pkgContext.startActivity(intent);
}
@Override
protected 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);
}
@Override
public void run() {
threadLocal.set(110);
int value = threadLocal.get();
System.out.println("SubThreadA:" + value);
}
}
class SubThreadB extends Thread {
public SubThreadB(String name) {
super(name);
}
@Override
public void run() {
int value = threadLocal.get();
System.out.println("SubThreadB:" + value);
}
}
}
//調用輸出
I: SubThreadA:110
I: MainThread:100
I: SubThreadB:999
複製代碼
最後經過調用輸出結果,能夠清晰的看到雖然操做的是同一個ThreadLocal對象,可是在三個不一樣線程存儲的值是互不影響的.回過頭再對照看概念性內容就清晰明瞭許多.另外在SubThreadB由於沒有設置值,因此得到的是咱們初始化設置的默認值999.oop
認識了它基本使用,接着一塊兒來了解ThreadLocal的基本原理,之因此用瞭解,由於筆者本身對ThreadLocal的理解有限且實際開發中運用較少.這裏就簡單的看下原理.post
對應源碼學習
public void set(T value) {
Thread t = Thread.currentThread();
//獲取當前線程的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
//不爲空,直接存儲值
map.set(this, value);
else
//爲空,建立一個ThreadLocalMap並存儲值.
createMap(t, value);
}
複製代碼
從上面源碼能夠看出,咱們設置的值,是存儲在ThreadLocalMap下的.當第一次存儲時,先會createMap初始化一個ThreadLocalMap.接着看如何進行初始化的.動畫
對應源碼
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數組中,該數組類型是Entry,擴展了弱引用封裝了ThreadLocal與value.
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
複製代碼
經過以上源碼,能夠很清楚的發現最終咱們設置的值,是存儲在一個Entry類型的table數組中的,這個Entry封裝了ThreadLocal與Value.
接着,若是不是第一次set值,那麼會進入ThreadLocalMap的set方法,咱們繼續往下看.
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數組中
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
複製代碼
這裏不分析具體算法,從上面能夠發現最終也是保存在table數組中.
對應源碼
public T get() {
Thread t = Thread.currentThread();
//一樣獲取當前線程的ThreadLocalMap
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();
}
複製代碼
經過以上源碼,get方法簡單來講就是從當前線程的ThreadLocalMap對象中獲取存儲的值.
經過SET與GET源碼簡單分析,咱們知道它們所操做的對象都是當前線程的ThreadLolocalMap對象的table數組,所以在不一樣線程中訪問同一個ThreadLocal的set和get方法,它們對ThreadLocal所作的讀寫操做僅限於各自線程的內部.
本文小結
最後咱們對ThreadLocal的實現流程作一個小結.
最後奉上一個極簡版的ThreadLocal的工做流程圖.
因爲本人技術有限,若有錯誤的地方,麻煩你們給我提出來,本人不勝感激,你們一塊兒學習進步.
參考連接: