スレッド間でデータを分離する
スレッド内のすべてのメソッド、スレッド内のすべてのメソッドにパラメータを渡すことを回避します。 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<SimpleDateFormat> 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 を取得します
は再実装されたハッシュ テーブルです。 2 つの要素に基づくハッシュ:
オブジェクト (例: dateFormatLocal
)。
value
をカプセル化するオブジェクト。
メソッドを通じて、現在の threadlocal## に基づいてハッシュ テーブル内の対応する
Entry# を取得します。 # object #get()
を初めて使用する場合は、
を使用して、ユーザーがオーバーライドした 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<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; // k: ThreadLocal, v: value Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
弱参照の一般的な使用法は次のとおりです: <pre class="brush:java;">WeakReference<RoleDTO> weakReference = new WeakReference<>(new RoleDTO());</pre>
したがって、
,
k は、弱参照である ThreadLocal
オブジェクトを表します。 v は、ThreadLocal
によって管理される value
を表し、これは強参照です。 メモリ リーク
がトリガーされ、プログラムがクラッシュします。 メモリ リークの問題は主にスレッド プールで発生します。これは、スレッド プール内のスレッドが継続的に実行され、実行のためにタスク キューから新しいタスクが継続的に取得されるためです。ただし、タスクには ThreadLocal
オブジェクトが存在する可能性があり、これらのオブジェクトの
はスレッドの ThreadLocalMap
に保存されるため、ThreadLocalMap
どんどん大きくなっていきます。 ただし、
ThreadLocal
はタスク (ワーカー) によって渡され、タスクの実行後、対応する
オブジェクトは破棄されます。スレッド内の関係は、Thread -> ThreadLoalMap -> Entry8cea09e86e6da166e71a296f2b1f24bd
です。 ThreadLocal
これは弱い参照であるため、GC 中に破棄され、ThreadLoalMap
に Entry5e33282b25ec20a016ad69d03248472f
が存在します。 remove()を使用します
スレッド プール内のスレッドは常に実行されているため、ThreadLoalMap がクリーンアップされていない場合は、
Entry2ce275f7b95bd469e229891ac13142eb は常にメモリを占有します。 remove()
メソッドは、key==null
の Entry
をクリアします。 静的変更を使用します
スレッド クラスをスレッド プールに複数回渡さないようにするには、ThreadLocal を
static に設定します。繰り返します。 エントリ
を作成します。たとえば、スレッド プールを使用して 10 個のタスクを処理するユーザー定義スレッド <pre class="brush:java;">public class Test implements Runnable{
private static ThreadLocal<Integer> local = new ThreadLocal<>();
@Override
public void run() {
// do something
}
}</pre>
があります。次に、Entry70cecb753554e47d1300fe45bef564fc
は、
Thread.ThreadLocalMap に保存されます。
キーワード、各スレッドの Entry
内のすべての local
変数は同じ変数を参照します。この時点でメモリ リークが発生したとしても、すべての Test クラスには local
オブジェクトが 1 つだけ存在するため、過度のメモリ使用が発生することはありません。 rree
以上がJavaにおけるThreadLocalの使用法と原理は何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。