在理解handler、looper之前,先來說說threadlocal這個類,聽名字好像是乙個本地執行緒的意思,實際上它並不是乙個thread,而是提供乙個與執行緒有關的區域性變數功能,每個執行緒之間的資料互不影響。我們知道使用handler的時候,每個執行緒都需要有乙個looper物件,那麼andorid中是怎麼儲存這個物件的呢,使用的就是threadlocal。
首先我們來看看主線程中looper是怎麼初始化的。
在應用啟動時,會線呼叫looper.preparemainlooper()方法,在這個方法裡面會去初始化主線程需要用的looper物件
static final threadlocalsthreadlocal = new threadlocal();
public static void preparemainlooper()
smainlooper = mylooper();
}}private static void prepare(boolean quitallowed)
//呼叫threadlocal的set()方法來儲存乙個looper物件
sthreadlocal.set(new looper(quitallowed));
}
我們看到在looper類中會在它被載入的時候將threadlocal物件建立出來,它是乙個靜態的變數。在我們初始化主線程的looper的時候,實際上就是直接new looper()然後將其放在了threadlocal中的。
下面我們將從set()方法作為入口來具體分析threadlocal是怎麼實現的。
public void set(t value)
threadlocalmap getmap(thread t)
void createmap(thread t, t firstvalue)
到這裡發現資料的具體儲存還是在threadlocalmap這個類中。
在說threadlocalmap之前先看看threadlocal裡面的乙個小東西。
//該hash值可以唯一確定乙個threadlocal物件,每建立乙個threadlocal物件,該hash值都是唯一的
private final int threadlocalhashcode = nexthashcode();
//原子類,保證多執行緒下唯一
private static atomicinteger nexthashcode =
new atomicinteger();
private static final int hash_increment = 0x61c88647;
private static int nexthashcode()
在threadlocal中還定義了如下的hash值,它在threadlocalmap中使用,可以唯一的確定乙個threadlocal物件。
我們接著看看threadlocalmap中的構造方法和set方法。
//entry是繼承自weakreference的軟引用,threadlocal作為key對它軟引用,
//同時也是乙個key-value的鍵值對
static class entry extends weakreference>
}private static final int initial_capacity = 16;
private entry table;
private int size = 0;
threadlocalmap(threadlocal<?> firstkey, object firstvalue)
firstkey.threadlocalhashcode & (initial_capacity - 1)
可能會讓人迷惑,實際上它和firstkey.threadlocalhashcode%initial_capacity
的計算結果是一樣的,保證index的值在0到initial_capacity之間,不包含initial_capacity
。但是這個前提是initial_capacity
的值必須為2n…。
2n的二進位制的表示為1000…,那麼2n-1的二進位制表示為0111…。是不是感覺好像發現了什麼?和2n-1求與剛好是將餘數部分給取出來,使用這種方式來計算index的速度要比直接使用%要快,但是是使用這個方式的前提就是initial_capacity的值必須為2n。
private void set(threadlocal<?> key, object value)
//如果發現有entry但是key被**了,則覆蓋
if (k == null)
}//如果找了一圈還是沒有找到entry,那麼就直接建立乙個entry新增進去
tab[i] = new entry(key, value);
int sz = ++size;
if (!cleansomeslots(i, sz) && sz >= threshold)
rehash();
}
這裡需要明白乙個地方:如果在當前index中找到了一樣的key,就直接覆蓋,如果找到了entry但是key被**了那麼就替換資料,如果key不一樣的話就在下乙個index繼續剛剛的判斷。只要在某乙個index中沒有找到entry物件,則直接建立乙個新的entry插入。
private void replacestaleentry(threadlocal<?> key, object value,int staleslot)
//如果找到舊資料,並且staleslot前面沒有舊資料,記錄當前i
if (k == null && slottoexpunge == staleslot)
slottoexpunge = i;
}//方便gc**它
tab[staleslot].value = null;
//使用乙個新的entry替換掉staleslot位置的舊資料
tab[staleslot] = new entry(key, value);
//清除其他位置的舊資料,staleslot被新資料給替換了
if (slottoexpunge != staleslot)
cleansomeslots(expungestaleentry(slottoexpunge), len);
}
當key被**的時候,會走到該方法中來。staleslot是乙個key被**的資料,我們無法外面獲取它,所以需要處理掉這些舊資料。這裡替換資料分為兩種情況:
1、如果在staleslot後面找到了相同的key,則在找到的地方覆蓋value同時和staleslot交換位置2、如果沒有找到key,就在staleslot重新建立新的entry覆蓋舊資料
private int expungestaleentry(int staleslot) else }}
return i;
}//清除staleslot後面的舊資料,每呼叫一次expungestaleentry(),從該方法返回值繼續清除後面的舊資料
private boolean cleansomeslots(int i, int n)
} while ( (n >>>= 1) != 0);
return removed;
}
在前面構造方法中呼叫了setthreshold(initial_capacity)
這個方法,設定乙個陣列大小的閾值,如果陣列中的資料個數超過了它那麼就呼叫rehash()
行擴充套件處理。
private void rehash()
//這裡會便利所有的元素來進行舊資料的清除處理
private void expungestaleentries()
}//重新計算大小,擴容處理在這裡面處理
private void resize() else }}
setthreshold(newlen);
size = count;
table = newtab;
}
set()
方法的分析到上面就結束了,有了上面的經驗,get()
方法分析起來就更簡單了。簡單說一下get()
方法。具體的說明看注釋
public t get()
}//返回初始化的值,如果不覆寫initialvalue()這裡的返回值就是null
return setinitialvalue();
}private t setinitialvalue()
最終還是會呼叫threadlocalmap的getentry()
方法
private entry getentry(threadlocal<?> key)
private entry getentryaftermiss(threadlocal<?> key, int i, entry e)
return null;
}
ThreadLocal實現原理與原始碼分析
threadlocal底層實現內部類 threadlocalmap 一 threadlocal的set方法原始碼分析 1 public void set t value thread t thread.currentthread threadlocalmap map getmap t if map ...
ThreadLocal原始碼理解
threadlocal其實原理是建立了多份相同資料儲存在堆記憶體上,每個執行緒的thread類裡有threadlocal.threadlocalmap threadlocals的屬性來指向存位置,所以每個執行緒修改都不會影響到其他執行緒的資料 首先說下下面用到的東西 threadlocalmap為t...
ThreadLocal 原始碼解讀
在正式讀 前先簡單介紹threadlocal的實現方式。每個執行緒都會有乙個threadlocalmap,只有在使用到threadlocal的時候才會初始化threadlocalmap。需要儲存的物件t會被放到entry裡面儲存在threadlocalmap的陣列中,entry是乙個鍵值對的資料結構...