FastThreadLocal
- Cada FastThread contém um FastThreadLocalMap e vários FastThreadLocals em cada FastThreadLocalThread ocupam índices diferentes.
- O primeiro elemento de cada InternalThreadLocalMap contém todos os objetos ThreadLocal. Os seguintes elementos contêm o valor correspondente a cada ThreadLocal
operação basica
pegar()
no tópico atual
- Encontre o índice de ThreadLocal e adicione o valor à posição de índice correspondente de InternalThreadLocalMap
definir()
no tópico atual
- Encontre o índice de ThreadLocal e adicione o valor à posição de índice correspondente de InternalThreadLocalMap
- Adicione ThreadLocal à coleção correspondente ao primeiro elemento do thread atual InternalThreadLocalMap
remover()
no tópico atual
- Encontre o índice de ThreadLocal e defina o valor correspondente ao índice em InternalThreadLocalMap para UNSET
- Exclua o ThreadLocal do primeiro conjunto de elementos do InternalThreadLocalMap do thread atual
ponto crítico de projeto
Compatível com ThreadLocal
Quando o encadeamento não usa FastThreadLocal, a lógica de ThreadLocal é usada por padrão.
tamanho inicial
O tamanho inicial é 32
algoritmo de hash
Use diretamente o auto-incremento global, não há conflito de Hash e o espaço é trocado por tempo
Quais são as condições de expansão? Como expandir?
- Condição de expansão de capacidade: o elemento de rosca atual excede a capacidade
- Expansão: O número de elementos é expandido para o número inteiro elevado à potência mais próxima de 2, por exemplo, 8 é considerado 5 e 64 é considerado 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;
}
Como evitar vazamentos de memória
Automatic: Use ftlt para executar uma tarefa Runnable agrupada por FastThreadLocalRunnable. Após a execução da tarefa, ftl será automaticamente limpo.
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 quanto InternalThreadLocalMap fornecem o método remove, e o usuário pode chamá-lo manualmente quando apropriado (às vezes é necessário, por exemplo, o pool de threads comuns usa ftl) para executar a exclusão de exibição.
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));
}
}
qual é o problema
1. Desperdício de espaço, o tamanho do array ThreadLocalMap de todos os threads é o mesmo.
Por exemplo, o thread 1 cria 100 objetos ThreadLocal. Thread 1 tem uma matriz de comprimento 100 dentro.
Neste ponto, o segundo thread precisa chamar o método get de ThreadLocal 100 e o segundo thread precisa alocar 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 precisa ser usado com FastThreadLocalThread, caso contrário, não é tão bom quanto ThreadLocal nativo.
3. FastThreadLocal usa o FastThreadLocalRunnable mais adequado, de modo que, após a execução da tarefa, ele chamará ativamente removeAll para remover todos