Java:ThreadLocal解析


序文

ThreadLocal は Java において非常に重要な内容であり、この記事ではメモリリークや重要な弱参照など、ThreadLocal の内容を可能な限り網羅的に紹介します。


1. ThreadLocal とは何ですか?

最初の説明: ThreadLocal はスレッド変数と呼ばれます。これは、ThreadLocal に設定された変数が現在のスレッドに属し、変数が他のスレッドから分離されていることを意味します。これは、変数が現在のスレッドに固有であることを意味します。ThreadLocal は各スレッドに変数のコピーを作成するため、各スレッドは独自の内部コピー変数にアクセスできます。

2 番目の説明: ThreadLocal はスレッド内で共有され、異なるスレッド間で分離されます。各スレッドは自分のスレッドの値のみを見ることができます。これが ThreadLocal の中心的な機能です。スレッドのローカル変数のスコープを実現することです。

3 番目の説明: ThreadLocal クラスの主な解決策は、各スレッドに独自の値をバインドさせることです。ThreadLocal クラスはデータを格納するボックスにたとえることができ、各スレッドのプライベート データをボックスに格納できます。

ThreadLocal 変数を作成すると、その変数にアクセスする各スレッドには変数のローカル コピーが作成され、ThreadLocal 変数名の由来となっています。get() メソッドと set() メソッドを使用してデフォルト値を取得したり、その値を現在のスレッドによって保存されたコピーの値に変更したりできるため、スレッドの安全性の問題を回避できます。

ここに画像の説明を挿入
ここに画像の説明を挿入
概要: スレッドスレッドは、ThreadLocal によって維持される、自身のスレッド専用の複数の共有変数を持つことができます (この共有変数は、自身のスレッド内でのみ共有されます)。
ここに画像の説明を挿入

ここに画像の説明を挿入

2、ThreadLocal のメモリリーク問題

1. メモリリークとは何ですか?

メモリ リークとは、プログラムがメモリを申請した後、要求されたメモリ領域を解放できないことを意味します。メモリ リークの害は無視できますが、メモリ リークの蓄積による影響は非常に深刻です。メモリの量に関係なく使用されるためです。遅かれ早かれアップします。
大まかに言うと、使用されなくなったオブジェクトや変数によって占有されていたメモリを再利用できないこと、つまりメモリ リークを意味します。

2. メモリ リークの問題が発生するのはなぜですか?

ここに画像の説明を挿入

ThreadLocal を不適切に操作するとメモリ リークが発生します。主な理由は、内部クラス ThreadLocalMap の Entry の設計にあります。

Entry は WeakReference<ThreadLocal<?>> を継承しています、つまり Entry のキーは弱参照です 弱参照の特徴は、このオブジェクトに弱参照しかない場合、次のガベージ コレクションでクリーンアップされることです。そのため、ガベージコレクション時にキーは再利用されますが、そのキーに対応する値は再利用されず、キーはnullで値は値を持つという現象が発生します。

キーが空の場合、値は無効なデータとなり、時間の経過とともに値が蓄積されるとメモリ リークが発生します。

ThreadLocal は、外部オブジェクトへの強参照がない場合の Thread と似ています。GC が発生すると、弱参照 Key はリサイクルされ、強参照の場合は Value はリサイクルされません。ThreadLocal を作成したスレッドが次のように実行され続ける場合、スレッド プール内のスレッド、次に Entry オブジェクト。値は決してリサイクルされないため、メモリ リークが発生する可能性があります。
ここに画像の説明を挿入

上の図からわかるように、hreadLocalMap は ThreadLocal の弱参照をキーとして使用します。ThreadLocal に外部の強参照がない場合、キー (ThreadLocal) は GC によってリサイクルされることになり、キーがThreadLocalMap は null になりますが、値はまだ存在します。 強参照があり、スレッド thread が終了した後でのみ、値の強参照チェーンが壊れます。

ただし、現在のスレッドが長時間終了しない場合、これらのエントリの値には、キーが null である強力な参照チェーンが常に存在します。

Thread Ref --> Thread  -->ThreaLocalMap  --> Entry -->value

つまり、リサイクルすることができず、メモリ リークが発生します。

特に強調する必要がある場合は、エントリのキー (threadlocal) への参照は弱参照、値への参照は強参照ですが、threadlocal への外部参照は強参照です。
したがって、threadlocal が外部参照される場合、強い参照があるため、threadlocal は GC によってリサイクルされませんが、threadlocal が外部参照されなくなると、つまり

 threadlocal = null

現時点では、スレッドローカルにはエントリの弱い参照しかなく、これはリサイクルされます。

3. メモリリークの問題を解決するにはどうすればよいですか?

(1) ThreadLocal はキーが null の値を自動的にクリアします

ThreadLocal の get()、set()、remove() は、スレッド ThreadLocalMap 内のキーが null であるすべての値をクリアします。

(2) 使用後に適切なタイミングで ThreadLocal.remove() を呼び出す

emove メソッドは、現在のキーと値 (Entry) をアクティブにクリアします。
ここに画像の説明を挿入

(3) ThreadLocalをグローバル変数として設定する

ThreadLocal は GC でリサイクルできないようにグローバル変数として設定されています (メンバー変数で使用する場合は、修飾子を public static に設定します。ThreadLocal はリサイクルされず、キーが null になることはありません)。メモリリークは発生しません)

3. Entryのキーを強参照として設定できますか?

無理です!

ThreadLocalMap のキーが強参照の場合、ThreadLocal をリサイクルするときに、ThreadLocalMap が ThreadLocal への強参照を保持したままであるため、手動で削除しないと ThreadLocal がリサイクルされず、Entry のメモリ リークが発生します。

ThreadlocalMap がスレッドにバインドされていることを知っておく必要があります。スレッドが破棄されず、スレッドローカルを参照しなくなった場合、キーと値のペアは常にマップ内に存在します。これは、プログラムに関する限り、メモリリークが発生しています。

この状況を回避するために、キーが弱い参照として設定されている限り、GC が発生すると、弱い参照は自動的にクリーンアップされます。つまり、ユーザー A が、メソッド、リリース threadlocalA が作成された後、弱い参照として、次のガベージ コレクションでクリーンアップされます。

さらに、ThreadLocalMap は内部の設定、取得、展開中にリークしたエントリをクリーンアップするため、メモリ リークについてあまり心配する必要はありません。

3. Entry の値を弱参照として設定できますか?

無理です!

値が弱参照として設計されている場合、値をフェッチする必要があるときに、フェッチされた値が null になる可能性が非常に高くなります。

ThreadLocal を使用する目的は、この値を現在のスレッドに保存し、必要なときに現在のスレッドから直接取得することです。つまり、強参照を保持している現在のスレッドに加えて、値も取得されます。理論的には、言い換えれば、他の強い参照があってはなりません。そうでない場合、現在のスレッドに値を保存することはできません。ただし、強参照されるべき値を弱参照に設計すると、jvm が gc 操作を実行する限り、値は直接リサイクルされます。現在のスレッドから値を取得する必要がある場合、null が返されます。

おすすめ

転載: blog.csdn.net/qq_46119575/article/details/131565307