高速スレッドローカル
- 各 FastThread には FastThreadLocalMap が含まれており、各 FastThreadLocalThread 内の複数の FastThreadLocal は異なるインデックスを占有します。
- 各 InternalThreadLocalMap の最初の要素には、すべての ThreadLocal オブジェクトが保持されます。次の要素は、各 ThreadLocal に対応する値を保持します。
基本操作
得る()
現在のスレッドで
- ThreadLocal のインデックスを見つけて、その値を InternalThreadLocalMap の対応するインデックス位置に追加します。
セット()
現在のスレッドで
- ThreadLocal のインデックスを見つけて、その値を InternalThreadLocalMap の対応するインデックス位置に追加します。
- 現在のスレッドの最初の要素に対応するコレクションに ThreadLocal を追加します InternalThreadLocalMap
取り除く()
現在のスレッドで
- ThreadLocal のインデックスを見つけて、InternalThreadLocalMap のインデックスに対応する値を UNSET に設定します。
- 現在のスレッドの InternalThreadLocalMap の最初の要素セットから ThreadLocal を削除します。
重要な設計ポイント
ThreadLocal との互換性
スレッドが FastThreadLocal を使用しない場合、デフォルトで ThreadLocal のロジックが使用されます。
初期サイズ
初期サイズは32です
ハッシュアルゴリズム
グローバル自己インクリメントを直接使用し、ハッシュの競合がなく、スペースと時間は交換されます。
拡張条件は何ですか?どのように拡張するか?
- 容量拡張条件: 現在のスレッド要素が容量を超えています
- 展開: 要素の数は、最も近い 2 のべき乗までの整数に展開されます。たとえば、5 の場合は 8 が取得され、31 の場合は 64 が取得されます。
public boolean setIndexedVariable(int index, Object value) {
Object[] lookup = indexedVariables;
// index 大于容量
if (index < lookup.length) {
Object oldValue = lookup[index];
lookup[index] = value;
return oldValue == UNSET;
} else {
// 扩容
expandIndexedVariableTableAndSet(index, value);
return true;
}
}
private void expandIndexedVariableTableAndSet(int index, Object value) {
Object[] oldArray = indexedVariables;
final int oldCapacity = oldArray.length;
int newCapacity;
// 当小于 2的30次方时,容量扩展为向上取最近 2 次幂整数,比如,5 取8,31 取 64。
if (index < ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD) {
newCapacity = index;
newCapacity |= newCapacity >>> 1;
newCapacity |= newCapacity >>> 2;
newCapacity |= newCapacity >>> 4;
newCapacity |= newCapacity >>> 8;
newCapacity |= newCapacity >>> 16;
newCapacity ++;
} else {
newCapacity = ARRAY_LIST_CAPACITY_MAX_SIZE;
}
Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
newArray[index] = value;
indexedVariables = newArray;
}
メモリリークを防ぐ方法
自動: ftlt を使用して、FastThreadLocalRunnable によってラップされた Runnable タスクを実行します。タスクの実行後、ftl は自動的にクリーンアップされます。
final class FastThreadLocalRunnable implements Runnable {
private final Runnable runnable;
private FastThreadLocalRunnable(Runnable runnable) {
this.runnable = ObjectUtil.checkNotNull(runnable, "runnable");
}
@Override
public void run() {
try {
runnable.run();
} finally {
FastThreadLocal.removeAll();
}
}
static Runnable wrap(Runnable runnable) {
return runnable instanceof FastThreadLocalRunnable ? runnable : new FastThreadLocalRunnable(runnable);
}
}
手動: ftl と InternalThreadLocalMap は両方とも、remove メソッドを提供しており、ユーザーは必要に応じて (通常のスレッドのスレッド プールが ftl を使用するなど、必要な場合もあります) 表示の削除を実行するために、このメソッドを手動で呼び出すことができます。
使用
final class FastThreadLocalRunnable implements Runnable {
private final Runnable runnable;
private FastThreadLocalRunnable(Runnable runnable) {
this.runnable = (Runnable)ObjectUtil.checkNotNull(runnable, "runnable");
}
public void run() {
try {
this.runnable.run();
} finally {
// 如果用的是 FastThreadLocalRunnable ,默认会做清理
FastThreadLocal.removeAll();
}
}
static Runnable wrap(Runnable runnable) {
return (Runnable)(runnable instanceof FastThreadLocalRunnable ? runnable : new FastThreadLocalRunnable(runnable));
}
}
どうしたの
1. スペースの無駄。すべてのスレッドの ThreadLocalMap 配列のサイズは同じです。
たとえば、スレッド 1 は 100 個の ThreadLocal オブジェクトを作成します。スレッド 1 の内部には長さ 100 の配列があります。
この時点で、2 番目のスレッドは ThreadLocal 100 の get メソッドを呼び出す必要があり、2 番目のスレッドは 100 個の Object オブジェクトを割り当てる必要があります。
import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.concurrent.FastThreadLocalThread;
import java.util.ArrayList;
import java.util.List;
public class FastThreadLocalTest {
private List<FastThreadLocal<String>> fastThreadLocals = new ArrayList<>();
private List<ThreadLocal<String>> threadLocals = new ArrayList<>();
void thread1Init() {
new Thread (() -> {
for (int i = 0; i < 31; i++) {
ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.get();
threadLocals.add(threadLocal);
}
}).start();
}
void thread2Init() {
new Thread(() -> {
threadLocals.get(threadLocals.size() - 1).get();
});
}
void fastThread1Init() {
new FastThreadLocalThread (() -> {
for (int i = 0; i < 33; i++) {
FastThreadLocal<String> fastThreadLocal = new FastThreadLocal<>();
fastThreadLocal.get();
fastThreadLocals.add(fastThreadLocal);
}
}).start();
}
void fastThread2Init() {
new FastThreadLocalThread(() -> {
fastThreadLocals.get(fastThreadLocals.size() - 1).get();
});
}
public static void main(String[] args) {
FastThreadLocalTest test = new FastThreadLocalTest();
test.fastThread1Init();
test.fastThread2Init();
test.thread1Init();
test.thread2Init();
}
}
2. FastThreadLocal は FastThreadLocalThread と一緒に使用する必要があります。そうしないと、ネイティブの ThreadLocal ほど優れた性能が得られません。
3. FastThreadLocal は、最も一致する FastThreadLocalRunnable を使用するため、タスクの実行後、アクティブに RemoveAll を呼び出してすべてを削除します。