Análisis del principio de FastThreadLocal

FastThreadLocal

fastthreadlocal

  1. Cada FastThread contiene un FastThreadLocalMap y varios FastThreadLocals en cada FastThreadLocalThread ocupan índices diferentes.
  2. 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

  1. Encuentre el índice de ThreadLocal y agregue el valor a la posición de índice correspondiente de InternalThreadLocalMap

colocar()

en el hilo actual

  1. Encuentre el índice de ThreadLocal y agregue el valor a la posición de índice correspondiente de InternalThreadLocalMap
  2. Agregue ThreadLocal a la colección correspondiente al primer elemento del hilo actual InternalThreadLocalMap

eliminar()

en el hilo actual

  1. Encuentre el índice de ThreadLocal y establezca el valor correspondiente al índice en InternalThreadLocalMap en UNSET
  2. 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

Prueba de estrés de rendimiento

mincrobench oficial de netty

Supongo que te gusta

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