JavaにおけるThreadLocalの使用法と原理は何ですか

王林
リリース: 2023-04-13 17:31:12
転載
971 人が閲覧しました

使用法

  • スレッド間でデータを分離する

  • スレッド内のすべてのメソッド、スレッド内のすべてのメソッドにパラメータを渡すことを回避します。 ThreadLocalで管理されているオブジェクトを取得します。

package com.example.test1.service;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

@Component
public class AsyncTest {

    // 使用threadlocal管理
    private static final ThreadLocal dateFormatLocal =
            ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

    // 不用threadlocal进行管理,用于对比
    SimpleDateFormat dateFormat = new SimpleDateFormat();

    // 线程名称以task开头
    @Async("taskExecutor")
    public void formatDateSync(String format, Date date) throws InterruptedException {
        SimpleDateFormat simpleDateFormat = dateFormatLocal.get();
        simpleDateFormat.applyPattern(format);
        
        // 所有方法都可以直接使用这个变量,而不用根据形参传入
        doSomething();
        
        Thread.sleep(1000);
        System.out.println("sync " + Thread.currentThread().getName() +  " | " + simpleDateFormat.format(date));
        
        // 线程执行完毕,清除数据
        dateFormatLocal.remove();
    }

    // 线程名称以task2开头
    @Async("taskExecutor2")
    public void formatDate(String format, Date date) throws InterruptedException {
        dateFormat.applyPattern(format);
        Thread.sleep(1000);
        System.out.println("normal " + Thread.currentThread().getName() +  " | " + dateFormat.format(date));
    }
}
ログイン後にコピー

junit を使用してテストします:

@Test
void test2() throws InterruptedException {
for(int index = 1; index <= 10; ++index){
String format = index + "-yyyy-MM-dd";
Date time = new Date();
asyncTest.formatDate(format, time);
}

for(int index = 1; index <= 10; ++index){
String format = index + "-yyyy-MM-dd";
Date time = new Date();
asyncTest.formatDateSync(format, time);
}
}
ログイン後にコピー

結果は次のとおりです。 によって管理されていない変数があることがわかります。 ThreadLocal は正しい形式と一致しませんでした。

同期タスク--10 | 10-2023-04-11
同期タスク--9 | 9-2023-04-11
通常タスク2-3 | 2-2023- 04-11
通常のタスク2-5 | 2-2023-04-11
通常のタスク2-10 | 2-2023-04-11
通常のタスク2-6 | 2-2023-04-11
同期タスク--1 | 1-2023-04-11
通常タスク2-7 | 2-2023-04-11
通常タスク2-8 | 2-2023-04-11
通常タスク2- 9 | 2-2023-04-11
同期タスク--6 | 6-2023-04-11
同期タスク--3 | 3-2023-04-11
同期タスク--2 | 2-2023-04-11
同期タスク--7 | 7-2023-04-11
同期タスク--4 | 4-2023-04-11
同期タスク--8 | 8- 2023-04-11
通常タスク2-4 | 2-2023-04-11
通常タスク2-1 | 2-2023-04-11
同期タスク--5 | 5-2023-04- 11
通常タスク2-2 | 2-2023-04-11

実装原則

ThreadLocal:

からデータを取得するプロセス

まず対応するスレッドを取得します。

getMap(t) を通じてスレッド内の ThreadLocalMap を取得します

#ThreadLocalMap

は再実装されたハッシュ テーブルです。 2 つの要素に基づくハッシュ:

    ユーザー定義の
  • ThreadLocal

    オブジェクト (例: dateFormatLocal)。

  • Entry

    value をカプセル化するオブジェクト。

map.getEntry(this)

メソッドを通じて、現在の threadlocal## に基づいてハッシュ テーブル内の対応する Entry# を取得します。 # object #get() を初めて使用する場合は、

setInitialValue()

を使用して、ユーザーがオーバーライドした initialValue() を呼び出します。 メソッド マップを作成し、ユーザー指定の値で初期化します。 この設計では、スレッドが終了すると、スレッド共有変数 ThreadLocalMap が破棄されます。

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}
ログイン後にコピー

Entry オブジェクトは弱参照です:

static class Entry extends WeakReference> {
    /** The value associated with this ThreadLocal. */
    Object value;

    // k: ThreadLocal, v: value
    Entry(ThreadLocal k, Object v) {
        super(k);
        value = v;
    }
}
ログイン後にコピー

弱参照の一般的な使用法は次のとおりです:

WeakReference weakReference = new WeakReference<>(new RoleDTO());
ログイン後にコピー
したがって、

では、 Entry

,

k

は、弱参照である ThreadLocal オブジェクトを表します。 v は、ThreadLocal によって管理される value を表し、これは強参照です。 メモリ リーク

メモリ リーク

とは、役に立たないオブジェクト (もう使用されなくなったオブジェクト) がメモリを占有し続けるか、役に立たないオブジェクトのメモリが時間内に解放されず、その結果、メモリ リーク スペースの浪費はメモリ リークと呼ばれます。ガベージ コレクターのアクティビティが増加し、メモリ使用量が増加し続けると、プログラムのパフォーマンスが徐々に低下し、極端な場合には、

OutOfMemoryError

がトリガーされ、プログラムがクラッシュします。 メモリ リークの問題は主にスレッド プールで発生します。これは、スレッド プール内のスレッドが継続的に実行され、実行のためにタスク キューから新しいタスクが継続的に取得されるためです。ただし、タスクには ThreadLocal オブジェクトが存在する可能性があり、これらのオブジェクトの

ThreadLocal

はスレッドの ThreadLocalMap に保存されるため、ThreadLocalMapどんどん大きくなっていきます。 ただし、ThreadLocal はタスク (ワーカー) によって渡され、タスクの実行後、対応する

ThreadLocal

オブジェクトは破棄されます。スレッド内の関係は、Thread -> ThreadLoalMap -> Entry です。 ThreadLocalこれは弱い参照であるため、GC 中に破棄され、ThreadLoalMapEntry が存在します。 remove()を使用します

スレッド プール内のスレッドは常に実行されているため、ThreadLoalMap がクリーンアップされていない場合は、

Entry< null, Object>

は常にメモリを占有します。 remove() メソッドは、key==nullEntry をクリアします。 静的変更を使用します

スレッド クラスをスレッド プールに複数回渡さないようにするには、ThreadLocal

static

に設定します。繰り返します。 エントリを作成します。たとえば、スレッド プールを使用して 10 個のタスクを処理するユーザー定義スレッド

public class Test implements Runnable{
    private static ThreadLocal local = new ThreadLocal<>();
    @Override
    public void run() {
        // do something
    }
}
ログイン後にコピー
があります。次に、Entry は、static が追加されたため、スレッド プール内のタスクの処理に使用される各スレッドの

Thread.ThreadLocalMap に保存されます。 キーワード、各スレッドの Entry 内のすべての local 変数は同じ変数を参照します。この時点でメモリ リークが発生したとしても、すべての Test クラスには local オブジェクトが 1 つだけ存在するため、過度のメモリ使用が発生することはありません。 rree

以上がJavaにおけるThreadLocalの使用法と原理は何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:yisu.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!