ThreadLocal原始碼分析

2021-08-11 16:35:34 字數 4631 閱讀 8204

在理解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是乙個鍵值對的資料結構...