FastThreadLocal の原理の分析

高速スレッドローカル

高速スレッドローカル

  1. 各 FastThread には FastThreadLocalMap が含まれており、各 FastThreadLocalThread 内の複数の FastThreadLocal は異なるインデックスを占有します。
  2. 各 InternalThreadLocalMap の最初の要素には、すべての ThreadLocal オブジェクトが保持されます。次の要素は、各 ThreadLocal に対応する値を保持します。

基本操作

得る()

現在のスレッドで

  1. ThreadLocal のインデックスを見つけて、その値を InternalThreadLocalMap の対応するインデックス位置に追加します。

セット()

現在のスレッドで

  1. ThreadLocal のインデックスを見つけて、その値を InternalThreadLocalMap の対応するインデックス位置に追加します。
  2. 現在のスレッドの最初の要素に対応するコレクションに ThreadLocal を追加します InternalThreadLocalMap

取り除く()

現在のスレッドで

  1. ThreadLocal のインデックスを見つけて、InternalThreadLocalMap のインデックスに対応する値を UNSET に設定します。
  2. 現在のスレッドの 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 を呼び出してすべてを削除します。

パフォーマンスストレステスト

netty公式ミニクロベンチ

おすすめ

転載: blog.csdn.net/wenxueliu/article/details/130919680