この記事の内容は、ThreadLocal の実装原理の分析と紹介です (コード付き)。必要な方は参考にしていただければ幸いです。
ThreadLocal はスレッド ローカル変数であり、それを使用するスレッドごとに変数の独立したコピーを維持するために使用されます。この変数は、スレッドのライフサイクル中のみ有効です。また、時間をスペースと引き換えにするロック メカニズムとは異なり、ThreadLocal には、変数のスレッドの安全性を確保するためにスペースを時間を引き換えにするロック メカニズムがありません。
この記事では、ThreadLocal の実装原理をソース コードから分析します。
まず ThreadLocal クラス図の構造を見てみましょう
SuppliedThreadLocal は主に JDK1.8 で Lambda 式のサポートを拡張するために使用されます。興味のある方は Baidu を参照してください。
ThreadLocalMap は ThreadLocal の静的な内部クラスであり、実際に変数を保存するクラスです。
Entry は、ThreadLocalMap の静的な内部クラスです。 ThreadLocalMap は、ThreadLocal をキー、変数を値として持つ Entry 配列を保持し、Entry をカプセル化します。
次の図は、Thread、ThreadLocal、ThreadLocalMap、および Entry の関係を簡単に説明したものです。
上の図について説明してください:
スレッドは ThreadLocalMap オブジェクトを所有します
ThreadLocalMap は Entry 配列を所有します
各エントリには k--v があります
エントリのキーは特定の ThreadLocal オブジェクトです
主なメソッドを以下で分析します。
1.set()
##
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
これはここでわかります: Thread には ThreadLocalMap オブジェクトが 1 つだけあります。特定の格納値は ThreadLocalMap の set() と呼ばれ、渡されるパラメータ キーは現在の ThreadLocal オブジェクトです。
ThreadLocalMap の set() メソッドをもう一度見てください:
private void set(ThreadLocal<?> key, Object value) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); // 1 for (Entry e = tab[i]; // 2 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); // 3 int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) // 4 rehash(); }
#キーのハッシュコードと配列容量の剰余を取得して配列インデックスを計算します -1
現在のインデックスからトラバースし、無効をクリアしますnull キーを持つエントリ
#private void resize() {
Entry[] oldTab = table;
int oldLen = oldTab.length;
int newLen = oldLen * 2;
Entry[] newTab = new Entry[newLen];
int count = 0;
for (int j = 0; j < oldLen; ++j) {
Entry e = oldTab[j];
if (e != null) {
ThreadLocal<?> k = e.get();
if (k == null) {
e.value = null; // Help the GC
} else {
int h = k.threadLocalHashCode & (newLen - 1);
while (newTab[h] != null)
h = nextIndex(h, newLen);
newTab[h] = e;
count++;
}
}
}
setThreshold(newLen);
size = count;
table = newTab;
}
2. get()
ThreadLocal の get() メソッドは次のとおりです。
##public T get() { Thread t = Thread.currentThread(); 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(); }
ThreadLocalMap の getEntry() メソッドは次のとおりです: ##
private Entry getEntry(ThreadLocal<?> key) { int i = key.threadLocalHashCode & (table.length - 1); // 1 Entry e = table[i]; if (e != null && e.get() == key) // 2 return e; else return getEntryAfterMiss(key, i, e); //3 } private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { Entry[] tab = table; int len = tab.length; while (e != null) { //4 ThreadLocal<?> k = e.get(); if (k == key) return e; if (k == null) expungeStaleEntry(i); else i = nextIndex(i, len); e = tab[i]; } return null; }
インデックスの計算
現在のインデックスのエントリが空ではなく、キーが同じです。直接戻ります。
それ以外の場合は、隣接するインデックスに移動して
を検索します。無効なキーが見つかった場合はクリアされます。見つかったらサイクルを終了します。
3. 削除()
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); } private void remove(ThreadLocal<?> key) { 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)]) { if (e.get() == key) { e.clear(); expungeStaleEntry(i); return; } } }処理方法は検索と保存と同様で、該当するエントリを削除した後、null キーを持つ無効な要素が削除されます。 #注
static class Entry extends WeakReference<ThreadLocal<?>> {}ThreadLocal には OOM の問題がある可能性があります。 ThreadLocalMap は ThreadLocal の弱参照をキーとして使用するため、GC が発生するとキーが再利用されるため、値自体がより大きなオブジェクトの場合、スレッドが終了しない場合は、null キーで value 要素にアクセスできません。値は、これまでリサイクルできなかったものになります。特にスレッド プールを使用する場合、スレッドは再利用され、スレッドは強制終了されません。このように、ThreadLocal の弱参照がリサイクルされるときに、値はリサイクルされません。 ThreadLocal を使用する場合、スレッド ロジック コードの終了時に ThreadLocal.remove() メソッドを明示的に呼び出す必要があります。
以上がThreadLocalの実装原理の分析と紹介(コード付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。