FastThreadLocal
- Cada FastThread contiene un FastThreadLocalMap y varios FastThreadLocals en cada FastThreadLocalThread ocupan índices diferentes.
- El primer elemento de cada InternalThreadLocalMap contiene todos los objetos ThreadLocal. Los siguientes elementos contienen el valor correspondiente a cada ThreadLocal
operación básica
conseguir()
en el hilo actual
- Encuentre el índice de ThreadLocal y agregue el valor a la posición de índice correspondiente de InternalThreadLocalMap
colocar()
en el hilo actual
- Encuentre el índice de ThreadLocal y agregue el valor a la posición de índice correspondiente de InternalThreadLocalMap
- Agregue ThreadLocal a la colección correspondiente al primer elemento del hilo actual InternalThreadLocalMap
eliminar()
en el hilo actual
- Encuentre el índice de ThreadLocal y establezca el valor correspondiente al índice en InternalThreadLocalMap en UNSET
- Elimine ThreadLocal del primer conjunto de elementos del InternalThreadLocalMap del subproceso actual
punto crítico de diseño
Compatible con ThreadLocal
Cuando el hilo no usa FastThreadLocal, la lógica de ThreadLocal se usa de manera predeterminada.
tamaño inicial
La talla inicial es 32
algoritmo hash
Use directamente el autoincremento global, no hay conflicto Hash y el espacio se intercambia por tiempo
¿Cuáles son las condiciones de expansión? ¿Cómo expandir?
- Condición de expansión de capacidad: el elemento de hilo actual excede la capacidad
- Expansión: el número de elementos se expande al número entero que se eleva a la potencia de 2 más cercana, por ejemplo, 8 se toma por 5 y 64 se toma por 31.
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;
}
Cómo evitar pérdidas de memoria
Automático: use ftlt para ejecutar una tarea Runnable empaquetada por FastThreadLocalRunnable. Después de ejecutar la tarea, ftl se limpiará automáticamente.
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);
}
}
Manualmente: tanto ftl como InternalThreadLocalMap proporcionan el método de eliminación, y el usuario puede llamarlo manualmente cuando corresponda (a veces es necesario, por ejemplo, el grupo de subprocesos ordinarios usa ftl) para realizar la eliminación de visualización.
usar
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));
}
}
Cuál es el problema
1. Pérdida de espacio, el tamaño de la matriz ThreadLocalMap de todos los subprocesos es el mismo.
Por ejemplo, el subproceso 1 crea 100 objetos ThreadLocal. El subproceso 1 tiene una matriz de longitud 100 en el interior.
En este punto, el segundo subproceso debe llamar al método get de ThreadLocal 100 y el segundo subproceso debe asignar 100 objetos 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 debe usarse con FastThreadLocalThread; de lo contrario, no es tan bueno como ThreadLocal nativo.
3. FastThreadLocal utiliza el FastThreadLocalRunnable que mejor se adapta, de modo que después de ejecutar la tarea, llamará activamente a removeAll para eliminar todo