ThreadLocalの原理と使用
22921 ワード
ThreadLocalはスレッド内部のローカル変数を提供するために使用され、この変数はスレッド間で互いに独立しており、ThreadLocalのsetメソッドとgetメソッドを呼び出すことで変数のアクセスを実現することができる.
基本的な使用方法:
しゅつりょく
以上のコードでは,ThreadLocalのinitialValue()メソッドを書き換えることでその初期値を設定し,メインスレッドではset()メソッドを呼び出して値を付与していないためget()メソッドを呼び出して得られる値がinitialValue()メソッドの戻り値である.ループ内のサブスレッドでは、ThreadLocalのset()メソッドを呼び出して各スレッドに値を付与すると、get()メソッドは、そのスレッドに設定された値であり、各スレッド間で互いに独立している.
ThreadLocalのset()メソッドは次のとおりです.
ThreadLocalのgetメソッドは次のとおりです.
ThreadLocaldのsetInitialValue()メソッドは次のとおりです.
ThreadLocalの最下位レベルでは、ThreadオブジェクトのThreadLocalMapを使用してデータストレージを行い、ThreadLocalでcreateMap(t,value)を呼び出すと、ThreadLocalMapの構築方法が呼び出されます.以下のようになります.
ThreadLocalのsetメソッドは、最終的にThreadLocalMapのset(ThreadLocal>key,Object value)メソッドを呼び出します.
上記のコードでは、まずキー(ThreadLocalオブジェクト)の値に基づいてその要素の格納位置を計算し、table配列を巡り、entryが空でない場合、キーを配列要素entryのthreadLocalオブジェクトと比較し、キー値がthreadLocalに対応する場合、見つかったentryオブジェクトのvalueに新しい値を割り当てます.entryのkeyが空の場合、replaceStaleEntryメソッドを呼び出してkeyが空のentryを現在設定するentryに置き換えます.配列を巡るときにentryが空の場合、新しいentryオブジェクトを作成してentryが空の位置に配置し、配列要素の個数に1を加え、最後にcleanSomeSlotsを呼び出して期限切れ要素を消去し、配列要素の個数がしきい値に達したかどうかを判断し、しきい値に達した場合、拡張を行います.
ThreadLocalのgetメソッドは、最終的にThreadLocalMapのgetEntry(ThreadLocal>key)メソッドを呼び出します.このメソッドは次のようになります.
上記のコードでは、まずkeyに対応するentryオブジェクトの格納位置を計算し、次に位置に基づいて配列からentryオブジェクトを取得し、entryオブジェクトがnullではなく、entryのkeyがメソッドに入力されたkeyと同じであればentryオブジェクトを返し、そうでなければgetEntryAfterMiss(key,i,e)メソッドを実行し、このメソッドでは配列中の要素を遍歴し、entryオブジェクトのkeyがnullでない場合、受信したkeyと比較し、同じようにentryオブジェクトを返します.entryオブジェクトのkeyがnullの場合、オブジェクトが期限切れであることを示す場合、expungeStaleEntryメソッドを呼び出して期限切れオブジェクトを消去します.
基本的な使用方法:
public class ThreadLocalTest {
private static ThreadLocal<String> threadLocal = new ThreadLocal<String>(){
@Override
protected String initialValue() {
return "initValue";
}
};
public static void main(String[] args) {
for (int i=0;i<5;i++){
int finalI = i;
new Thread(new Runnable() {
@Override
public void run() {
threadLocal.set("thread-"+finalI);
System.out.println(threadLocal.get());
threadLocal.remove();
System.out.println(threadLocal.get());
}
}).start();
}
System.out.println("main-" + threadLocal.get());
}
}
しゅつりょく
thread-0
thread-2
thread-3
main-initValue
thread-4
thread-1
以上のコードでは,ThreadLocalのinitialValue()メソッドを書き換えることでその初期値を設定し,メインスレッドではset()メソッドを呼び出して値を付与していないためget()メソッドを呼び出して得られる値がinitialValue()メソッドの戻り値である.ループ内のサブスレッドでは、ThreadLocalのset()メソッドを呼び出して各スレッドに値を付与すると、get()メソッドは、そのスレッドに設定された値であり、各スレッド間で互いに独立している.
ThreadLocalのset()メソッドは次のとおりです.
public void set(T value) {
//
Thread t = Thread.currentThread();
// ThreadLocalMap
ThreadLocalMap map = getMap(t);
// map ,
if (map != null)
map.set(this, value);
else
// map , ThreadLocalMap value map
createMap(t, value);
}
ThreadLocalのgetメソッドは次のとおりです.
public T get() {
//
Thread t = Thread.currentThread();
// ThreadLocalMap
ThreadLocalMap map = getMap(t);
// map , ThreadLocal key, map getEntry Entry
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
// entry , entry value
T result = (T)e.value;
return result;
}
}
// map , setInitialValue
return setInitialValue();
}
ThreadLocaldのsetInitialValue()メソッドは次のとおりです.
private T setInitialValue() {
// ,initialValue() null,
T value = initialValue();
//
Thread t = Thread.currentThread();
// ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
// map ,
map.set(this, value);
else
// map , ThreadLocalMap value map
createMap(t, value);
return value;
}
ThreadLocalの最下位レベルでは、ThreadオブジェクトのThreadLocalMapを使用してデータストレージを行い、ThreadLocalでcreateMap(t,value)を呼び出すと、ThreadLocalMapの構築方法が呼び出されます.以下のようになります.
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
// Entry , 16
table = new Entry[INITIAL_CAPACITY];
// value
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
// Entry ,key ThreadLocal , value,
table[i] = new Entry(firstKey, firstValue);
//
size = 1;
// , table * 2/3;
setThreshold(INITIAL_CAPACITY);
}
ThreadLocalのsetメソッドは、最終的にThreadLocalMapのset(ThreadLocal>key,Object value)メソッドを呼び出します.
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;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
上記のコードでは、まずキー(ThreadLocalオブジェクト)の値に基づいてその要素の格納位置を計算し、table配列を巡り、entryが空でない場合、キーを配列要素entryのthreadLocalオブジェクトと比較し、キー値がthreadLocalに対応する場合、見つかったentryオブジェクトのvalueに新しい値を割り当てます.entryのkeyが空の場合、replaceStaleEntryメソッドを呼び出してkeyが空のentryを現在設定するentryに置き換えます.配列を巡るときにentryが空の場合、新しいentryオブジェクトを作成してentryが空の位置に配置し、配列要素の個数に1を加え、最後にcleanSomeSlotsを呼び出して期限切れ要素を消去し、配列要素の個数がしきい値に達したかどうかを判断し、しきい値に達した場合、拡張を行います.
ThreadLocalのgetメソッドは、最終的にThreadLocalMapのgetEntry(ThreadLocal>key)メソッドを呼び出します.このメソッドは次のようになります.
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
上記のコードでは、まずkeyに対応するentryオブジェクトの格納位置を計算し、次に位置に基づいて配列からentryオブジェクトを取得し、entryオブジェクトがnullではなく、entryのkeyがメソッドに入力されたkeyと同じであればentryオブジェクトを返し、そうでなければgetEntryAfterMiss(key,i,e)メソッドを実行し、このメソッドでは配列中の要素を遍歴し、entryオブジェクトのkeyがnullでない場合、受信したkeyと比較し、同じようにentryオブジェクトを返します.entryオブジェクトのkeyがnullの場合、オブジェクトが期限切れであることを示す場合、expungeStaleEntryメソッドを呼び出して期限切れオブジェクトを消去します.