スレッドローカル変数交換フレームワーク - TransmitterableThreadLocal (Alibaba オープンソース)

上:数秒で 100 万に達する高同時実行フレームワーク - Disruptor


TransmitterableThreadLocal の概要

    TTL と呼ばれる TransmitterableThreadLocal は、Alibaba のオープンソース フレームワークです。TransmittableThreadLocal は、Java の ThreadLocal を拡張および拡張します。これは、ThreadLocal がスレッド プールまたは非同期タスク呼び出しチェーンで値を正しく渡せないという問題を解決するように設計されています。

TransmitterableThreadLocal ソースコード: https://github.com/alibaba/transmittable-thread-local

754a9076972d6129c7fc57468b2aaacb.png

TransmitterableThreadLocal はどのような問題を解決しますか?

通常、スレッドプールや非同期タスクを使用する場合、元の ThreadLocal では、スレッドの切り替えやタスクの実行時に、子スレッドや後続のタスクに設定値を正しく渡すことができません。このため、スレッド プールまたは非同期プログラミングを使用する場合、ThreadLocal の使用が非常に複雑になります。TransmittableThreadLocal は、スレッドを切り替えるときに ThreadLocal の値を明示的に渡すことで、上記の問題を解決します。これは、ThreadLocal の値を親スレッドから子スレッドに自動的に渡すメカニズムを提供し、タスク呼び出しチェーン全体で正しく渡されるようにします。

TransmittableThreadLocal を使用すると、通常の ThreadLocal を使用するのと同じように値を設定および取得できますが、値の受け渡しは自動的に処理されます。このようにして、スレッド プールや非同期タスクでも ThreadLocal 値を共有して正しく渡すことができます。

個人的な理解: これは実際には元のスレッド プールの拡張バージョンであり、前のスレッドから次のスレッドに一部の値を転送する問題を解決します。

TransmitterableThreadLocal の使用

jar パッケージをインポートする

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.14.2</version>
</dependency>

デモコードを検証する

デモ1

package com.ttl;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.alibaba.ttl.TransmittableThreadLocal;

public class MyRunnable implements Runnable {
    private static TransmittableThreadLocal<Integer> threadLocal = new TransmittableThreadLocal<>();

    @Override
    public void run() {
        threadLocal.set((int) (Math.random() * 100)); // 设置当前线程的值
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("TransmittableThreadLocal value: " + threadLocal.get()); // 获取当前线程的值
    }
}
package com.ttl;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author: csh
 * @Date: 2023/7/8 20:37
 * @Description:
 * ttl案例,通过 线程池,将上一个线程带给下一线程。
 *
 */
public class StudyTtl {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        MyRunnable sharedRunnable = new MyRunnable();

        executorService.execute(sharedRunnable);
        executorService.execute(sharedRunnable);

        executorService.shutdown();
    }
}

結果

TransmittableThreadLocal value: 99
TransmittableThreadLocal value: 25

デモ2

package com.ttl;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.alibaba.ttl.TransmittableThreadLocal;

public class TransmittableThreadLocalDemo {
    public static void main(String[] args) {
        // 创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        // 创建TransmittableThreadLocal实例
        TransmittableThreadLocal<Integer> threadLocal = new TransmittableThreadLocal<>();

        // 在主线程设置值
        threadLocal.set(42);
        System.out.println("主线程 - ThreadLocal value: " + threadLocal.get());

        // 提交任务到线程池
        executorService.execute(() -> {
            // 打印子线程中的ThreadLocal值
            System.out.println("子线程 - ThreadLocal value: " + threadLocal.get());
        });

        // 关闭线程池
        executorService.shutdown();
    }
}

結果

主线程 - ThreadLocal value: 42
子线程 - ThreadLocal value: 42

TransmitterableThreadLocalの実装原理

TransmittableThreadLocal の実装原理は主に Java の InheritableThreadLocal と ThreadLocal に依存しています。

Javaでは、InheritableThreadLocalは親スレッドと子スレッド間で値を受け渡すことができるクラスです。子スレッドを通じて親スレッドの値を継承し、子スレッドで値を変更できます。ただし、InheritableThreadLocal は、スレッドの作成時にのみ値を継承し、スレッドの切り替えまたはタスクの実行後に値を正しく渡すことができないため、スレッド プールまたは非同期タスクのシナリオのニーズを満たしません。

TransmittableThreadLocal は、InheritableThreadLocal クラスを拡張し、その関連メソッドを書き換えることにより、ThreadLocal 値転送の機能強化を実装します。

具体的には、TransmittableThreadLocal の実装原則は次のとおりです。

TransmittableThreadLocal では、各 ThreadLocal オブジェクトに対応する Holder オブジェクト (TransmitterableThreadLocal.TransmitterableThreadLocalHolder など) があり、値の転送を保存するために使用されます。

メイン スレッドが ThreadLocal 値を設定すると、TransmittableThreadLocal はその値を Holder オブジェクトに保存します。

子スレッドを作成するとき、TransmittableThreadLocal は InheritableThreadLocal の特性を使用して、親スレッドの Holder オブジェクトを子スレッドにコピーします。

子スレッドでは、TransmittableThreadLocal を通じて値を取得するときに、まず現在のスレッドに Holder オブジェクトがあるかどうかを確認します。そうでない場合は、値が正しく転送されるように、Holder オブジェクトが親スレッドから取得され、子スレッドにコピーされます。

親スレッドの ThreadLocal 値が変更されると、TransmittableThreadLocal は、値の一貫性を維持するために、子スレッドの対応する値も同期的に更新します。

要約すると、TransmittableThreadLocal の実装メカニズムは主に InheritableThreadLocal クラスを継承し、関連するメソッドを書き換え、値の転送と同期更新に Holder オブジェクトを使用します。これにより、スレッド切り替えまたは非同期タスク中に ThreadLocal 値を正しく渡すことができます。

TransmitterableThreadLocal コア ソース コードの学習

クラス図

869f087c32eb93bcd3c836e9eb833d3a.png

com.alibaba.ttl.TransmittableThreadLocal は次のように実装されます

public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> {
    private transient ConcurrentMap<Thread, T> threadLocalValues = new ConcurrentHashMap<>();
    private transient ConcurrentMap<Thread, ThreadLocalMap> inheritableThreadLocals = new ConcurrentHashMap<>();

    @Override
    public void set(T value) {
        Thread currentThread = Thread.currentThread();

        // 检查当前线程是否存在 ThreadLocalMap 对象,如果不存在,则创建
        ThreadLocalMap threadLocals = inheritableThreadLocals.get(currentThread);
        if (threadLocals == null) {
            threadLocals = new ThreadLocalMap();
            inheritableThreadLocals.put(currentThread, threadLocals);
        }

        // 设置当前线程的值
        threadLocals.set(this, value);

        // 存储当前线程的值
        threadLocalValues.put(currentThread, value);
    }
    //初始化值方法
    @Override
    protected T initialValue() {
        return null;
    }
    //获取父类的值
    @Override
    protected T childValue(T parentValue) {
        return parentValue;
    }
    //获取方法
    @Override
    public T get() {
        Thread currentThread = Thread.currentThread();

        // 首先尝试从 threadLocalValues 获取当前线程的值
        T value = threadLocalValues.get(currentThread);
        if (value == null && !threadLocalValues.containsKey(currentThread)) {

            // 如果 threadLocalValues 中不存在当前线程的值,则尝试从 inheritableThreadLocals 获取
            ThreadLocalMap threadLocals = inheritableThreadLocals.get(currentThread);
            if (threadLocals != null) {
                value = threadLocals.get(this);
            }

            // 如果还是没有获取到值,则调用 initialValue() 方法初始化
            if (value == null) {
                value = initialValue();

                // 将值存储到 threadLocalValues 和 inheritableThreadLocals 中
                threadLocalValues.put(currentThread, value);
                if (threadLocals == null) {
                    threadLocals = new ThreadLocalMap();
                    inheritableThreadLocals.put(currentThread, threadLocals);
                }
                threadLocals.set(this, value);
            }
        }
        return value;
    }
    //删除当前线程的内容
    @Override
    public void remove() {
        Thread currentThread = Thread.currentThread();
        threadLocalValues.remove(currentThread);

        ThreadLocalMap threadLocals = inheritableThreadLocals.get(currentThread);
        if (threadLocals != null) {
            threadLocals.remove(this);
        }
    }
}

他のソースコードは自分で理解できます~

やっと

    ttl ボックスは通常、リンク トラッキング テクノロジのシナリオで使用されます。現在のスレッドによって生成された taskid を次のスレッドに渡し、次のスレッドがそれを処理します。または、一部の特殊なオンライン ビジネス シナリオでは 1 つ以上のスレッドが必要です。順次配信ですが、1 つのスレッド内のみの場合は、マシンの再起動が必要になったり、考慮されていない操作が必要になる可能性が非常に高いため、新しいフィールドまたは Redis などの統合キャッシュを追加してこの操作を実行することをお勧めします。 , 操作の価値が失われ、それをビジネス コード (特にビジネス コード) に結合するのは不合理です。それが本当に必要でない場合、またはより良い解決策がある場合でも、シナリオが適切かどうかを検討する必要があります。それは、自分のスキルを誇示するために、プロジェクトのコードをメンテナンスしたり、トラブルシューティングが難しいオンライン事故を見つけたりする学生をあまりにも多く見てきたからです~~~

参考文献:

https://segmentfault.com/a/1190000041954190?utm_source=sf-similar-article

https://github.com/alibaba/transmittable-thread-local

おすすめ

転載: blog.csdn.net/qq_16498553/article/details/131618455