Análise do princípio de FastThreadLocal

FastThreadLocal

fastthreadlocal

  1. Cada FastThread contém um FastThreadLocalMap e vários FastThreadLocals em cada FastThreadLocalThread ocupam índices diferentes.
  2. 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

  1. Encontre o índice de ThreadLocal e adicione o valor à posição de índice correspondente de InternalThreadLocalMap

definir()

no tópico atual

  1. Encontre o índice de ThreadLocal e adicione o valor à posição de índice correspondente de InternalThreadLocalMap
  2. Adicione ThreadLocal à coleção correspondente ao primeiro elemento do thread atual InternalThreadLocalMap

remover()

no tópico atual

  1. Encontre o índice de ThreadLocal e defina o valor correspondente ao índice em InternalThreadLocalMap para UNSET
  2. 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

Teste de estresse de desempenho

netty oficial mincrobench

Acho que você gosta

Origin blog.csdn.net/wenxueliu/article/details/130919680
Recomendado
Clasificación