【Javaコンカレントプログラミング】ThreadLocalスレッドローカルクラス

 1.はじめに

ThreadLocal クラスは、スレッド内のローカル変数として提供されます。これらの変数がマルチスレッド環境でアクセス (取得/設定) されると、各スレッドの変数が他のスレッドの変数から比較的独立していることを確認できます。

ThreadLocal を介して作成された変数は、現在のスレッドのみがアクセスでき、他のスレッドからは見えないため、他のスレッドが変数にアクセスして変更することはできません。つまり、スレッドの公開はプライベート スレッドになります。実際、各スレッドには ThreadLocal 変数のコピーがあります。

ロックと比較したパフォーマンス:

ロックの使用に Java を使用すると、作業効率が低下しますが、ThreadLocal を使用すると、効率に影響を与えることなく、共有リソースの競合を完全に回避できます。ThreadLocal は「時間と空間を交換する」という方法を採用し、各スレッドに変数のコピーを提供して、相互に影響を与えることなく同時アクセスを実現しますが、各スレッドがコピーを維持するため、メモリ使用量が増加します。

二、ThreadLocal原則

ThreadLocalMap は、ThreadLocal クラスで定義された静的内部クラスです。そして、その構造は HashMap 構造に非常に似ています。データを格納する Entry[] 配列があります。そして、この Entry クラスは WeakReference クラスから継承されたサブクラスであり、HashMap とは異なります。

  

メソッドを取得

get() メソッドは、Thread から ThreadLocalMap を取り出し、ThreadLocal オブジェクトを Key として値を取り出すメソッドであり、remove() メソッドは、ThreadLocalMap を取り出し、ThreadLocal に対応するデータを削除するメソッドです。

セット方法

set メソッドが呼び出されると、データが threadLocals Map オブジェクトに書き込まれます. この Map のキーは現在の ThreadLocal オブジェクトであり、値は保存した値です. また、threadLocals 自体は複数の ThreadLocal オブジェクトを保存できます。これは、ThreadLocal コレクションと同等です。

ハッシュマップの連結リスト方式とは異なり、ハッシュの競合を解決するために「オープンアドレス方式」が使用されます。

欠点

メモリリークの問題

ThreadLocalMap の Entry の Key は弱参照なので、使用後に remove メソッドを呼び出してクリアしないと、対応する値のメモリ リークが発生します。そのため、使用後は必ず remove メソッドを呼び出してデータをクリアしてください。

3、ThreadLocal の使用

public class Main {
    //1、定义一个private static的ThreadLocal对象。
    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        //2、每个线程可以将当前线程需要存放在局部变量中,并且可以从中获取。
        threadLocal.set("");
        String s = threadLocal.get();

        //3、最后在使用完之后,需要将ThreadLocal中的值移除。
        threadLocal.remove();
    }
}

使用シーン

主にスレッド間のデータ分離に使用されます。

例: ツール クラス SimpleDateFormat はスレッド セーフではありません。スレッド セーフを確保するために、ThreadLocal を介して各スレッドにコピーを配置できます。

4、FastThreadLocal クラス

FastThreadLocal は netty パッケージ、具体的には io.netty.util.concurrent パッケージです

1、FastThreadLocal由来

netty が FastThreadLocal クラスを再作成するのはなぜですか?

ThreadLocal は効率的ではないため、具体的には次のとおりです。

  • 現在の Thread にバインドされた変数は、ThreadLocal ハッシュに従って Map 内で検索されますが、これは配列添字アクセスほど高速ではありません。

  • オープン アドレス方式を使用してハッシュの競合を解決する場合、効率は非常に低くなります。つまり、ハッシュのインデックスが占有されている場合、それを格納する場所があるかどうかを振り返り、スペースができるまでそれを繰り返します。

  • マップ展開は、インデックスの一部を再計算し、最悪の場合、要素の位置を移動します

  • スレッドが実行されたら、バインドされたスレッド ローカル変数をクリアするのが最善です。そうしないと、メモリ リークが発生します。実際には、クリーンアップを忘れることがよくあります。

FastThreadLocal (以降、ftl と呼びます) は、配列を直接使用して、ハッシュの競合を回避します。

具体的な方法は、各 FastThreadLocal インスタンスが作成されるときに添え字インデックスが割り当てられ、割り当てインデックスは AtomicInteger を使用して実装され、各 FastThreadLocal は一意の添え字を取得できます。ftl.get() メソッドを呼び出して値を取得する場合は、次の図に示すように、return array[index] のように、配列から直接戻り値を取得します。

ThreadLocal クラスに対応

ネイティブの ThreadLocal、Thread、ThreadLocal、および ThreadLocalMap には 3 つの役割があります。Netty の FastThreadLocalThread、FastThreadLocal、および InternalThreadLocalMap にそれぞれ対応します。

2.使い方

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
</dependency>

        //1、Netty提供的DefaultThreadFactory 工厂类,创建的线程默认就是 FastThreadLocalThread 类型
        DefaultThreadFactory defaultThreadFactory = new DefaultThreadFactory(FastThreadLocalTest.class);

        //2、创建FastThreadLocal
        FastThreadLocal<String> fastThreadLocal = new FastThreadLocal<>();

        //3、工厂生产线程
        Thread thread = defaultThreadFactory.newThread(() -> {
            //4、设置变量
            fastThreadLocal.set("");
            //5、获取变量
            fastThreadLocal.get();
        });
        //6、启动线程
        thread.start();

おすすめ

転載: blog.csdn.net/sumengnan/article/details/125070829
おすすめ