Java ThreadLocalクラスの使い方
#図に示すように:
#クイック スタート
package cuit.pymjl.thradlocal;
/**
* @author Pymjl
* @version 1.0
* @date 2022/7/1 10:56
**/
public class MainTest {
static ThreadLocal<String> threadLocal = new ThreadLocal<>();
static void print(String str) {
//打印当前线程中本地内存中本地变量的值
System.out.println(str + " :" + threadLocal.get());
//清除本地内存中的本地变量
threadLocal.remove();
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
//设置线程1中本地变量的值
threadLocal.set("thread1 local variable");
//调用打印方法
print("thread1");
//打印本地变量
System.out.println("after remove : " + threadLocal.get());
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
//设置线程1中本地变量的值
threadLocal.set("thread2 local variable");
//调用打印方法
print("thread2");
//打印本地变量
System.out.println("after remove : " + threadLocal.get());
}
});
t1.start();
t2.start();
}
}
ThreadLocal の原理
ThreadLocal 関連クラス図
まず、次に示すように、ThreadLocal 関連クラスのクラス図構造を見てみましょう。 この図からわかるように、Thread クラスには threadLocals と継承可能なThreadLocals があり、どちらも ThreadLocalMap 型の変数です。 、ThreadLocalMap はカスタマイズされたハッシュマップです。デフォルトでは、各スレッドのこれら 2 つの変数は null であり、現在のスレッドが初めて ThreadLocal の set メソッドまたは get メソッドを呼び出したときにのみ作成されます。実際、各スレッドのローカル変数は ThreadLocal インスタンスではなく、呼び出しスレッドの threadLocals 変数に格納されます。つまり、ThreadLocal 型のローカル変数は、特定のスレッド メモリ空間に格納されます。 ThreadLocal は、set メソッドを通じて呼び出しスレッドの threadLocals に値を入れて保存するツール シェルです。呼び出しスレッドが get メソッドを呼び出すと、現在のスレッドの threadLocals 変数から値を取り出して使用します。呼び出しスレッドが終了しない場合、このローカル変数は常に呼び出しスレッドの threadLocals 変数に格納されます。したがって、ローカル変数が必要ないときは、次のコマンドを呼び出して、現在のスレッドの threadLocals からローカル変数を削除できます。 ThreadLocal 変数のメソッドを削除します。さらに、Thread の threadLocals がマップ構造として設計されているのはなぜですか?各スレッドを複数の ThreadLocal 変数に関連付けることができるため、これは明らかです。次に、ThreadLocal の set、get、remove のソース コードを見てみましょう。
set
public void set(T value) { // 1.获取当前线程(调用者线程) Thread t = Thread.currentThread(); // 2.以当前线程作为key值,去查找对应的线程变量,找到对应的map ThreadLocalMap map = getMap(t); if (map != null) { // 3.如果map不为null,则直接添加元素 map.set(this, value); } else { // 4.否则就先创建map,再添加元素 createMap(t, value); } }
void createMap(Thread t, T firstValue) { /** * 这里是创建一个ThreadLocalMap,以当前调用线程的实例对象为key,初始值为value * 然后放入当前线程的Therad.threadLocals属性里面 */ t.threadLocals = new ThreadLocalMap(this, firstValue); }
ThreadLocalMap getMap(Thread t) { //这里就是直接获取调用线程的成员属性threadlocals return t.threadLocals; }
get
public T get() { // 1.获取当前线程 Thread t = Thread.currentThread(); // 2.获取当前线程的threadlocals,即ThreadLocalMap ThreadLocalMap map = getMap(t); // 3.如果map不为null,则直接返回对应的值 if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } // 4.否则,则进行初始化 return setInitialValue(); }
次は、
setInitialValue のコードです。##
private T setInitialValue() { //初始化属性,其实就是null T value = initialValue(); //获取当前线程 Thread t = Thread.currentThread(); //通过当前线程获取ThreadLocalMap ThreadLocalMap map = getMap(t); //如果map不为null,则直接添加元素 if (map != null) { map.set(this, value); } else { //否则就创建,然后将创建好的map放入当前线程的属性threadlocals createMap(t, value); } //将当前ThreadLocal实例注册进TerminatingThreadLocal类里面 if (this instanceof TerminatingThreadLocal) { TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this); } return value; }
ここでいくつか説明を追加する必要があります
TerinatedThreadLocal。このクラスは jdk11 で新しく追加されたもので、jdk8 には存在しないため、インターネット上の多くのソース コード分析ではこのクラスに関連する説明がありません。このクラスのソース コードを確認したところ、その機能は ThreadLocal メモリ リークの問題を回避することになっているはずです (興味があれば、ソース コードを見て、間違いがあれば修正してください) )。これが公式の説明です: /** * A thread-local variable that is notified when a thread terminates and * it has been initialized in the terminating thread (even if it was * initialized with a null value). * 一个线程局部变量, * 当一个线程终止并且它已经在终止线程中被初始化时被通知(即使它被初始化为一个空值)。 */
remove
public void remove() { //如果当前线程的threadLocals 变量不为空, 则删除当前线程中指定ThreadLocal 实例的本地变量。 ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) { m.remove(this); } }
、null キーを使用してこれらのエントリの値にアクセスする方法はありません。
現在のスレッドが長時間終了しない場合、常に存在します。参照チェーン: Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value は決してリサイクルできず、メモリ リークが発生します。実際、この状況は ThreadLocalMap の設計で考慮されており、いくつかの保護措置が追加されています。スレッド ThreadLocalMap 内の null であるすべてのキーは、get()、set()、および Remove の実行中にクリアされます。 ThreadLocal.値の()。ただし、これらの受動的な予防策では、メモリ リークが発生しないことを保証できません。静的 ThreadLocal を使用すると、ThreadLocal のライフ サイクルが延長され、メモリ リークが発生する可能性があります。
割り当てでは ThreadLocal が使用され、メモリ リークにつながる get()、set()、remove() メソッドは呼び出されなくなりました。
弱参照を使用する理由
弱い参照を使用すると ThreadLocalMap のメモリ リークが発生することは誰もが知っているのに、なぜ当局は依然として強参照ではなく弱い参照を使用するのでしょうか?これは、弱参照と強参照の使用の違いから始まります。
強参照を使用する場合: ThreadLocalMap のライフ サイクルは基本的に Thread のライフ サイクルと同じであることがわかっています。現在のスレッドが終了しない場合、ThreadLocalMap は GC によってリサイクルされません。と ThreadLocalMap が ThreadLocal の権利を保持します。強い参照を行うと、ThreadLocal はリサイクルされません。スレッドのライフサイクルが長い場合、手動で削除しないと kv が蓄積され、OOM
# が発生します。 - ##弱い参照を使用する場合: 弱い 参照内のオブジェクトの宣言期間は短いです。これは、システム GC 中に、弱い参照が見つかった限り、ヒープ領域が残っているかどうかに関係なく、オブジェクトがリサイクルされるためです。十分な。 ThreadLocal の強参照を再利用すると、ThreadLocalMap が保持する弱参照も再利用されるため、手動で kv を削除しないと値が蓄積され、OOM
remove()
メソッドを呼び出します。以上がJava ThreadLocalクラスの使い方の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









Java の乱数ジェネレーターのガイド。ここでは、Java の関数について例を挙げて説明し、2 つの異なるジェネレーターについて例を挙げて説明します。

Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

Java での日付までのタイムスタンプに関するガイド。ここでは、Java でタイムスタンプを日付に変換する方法とその概要について、例とともに説明します。

Java は、初心者と経験豊富な開発者の両方が学習できる人気のあるプログラミング言語です。このチュートリアルは基本的な概念から始まり、高度なトピックに進みます。 Java Development Kit をインストールしたら、簡単な「Hello, World!」プログラムを作成してプログラミングを練習できます。コードを理解したら、コマンド プロンプトを使用してプログラムをコンパイルして実行すると、コンソールに「Hello, World!」と出力されます。 Java の学習はプログラミングの旅の始まりであり、習熟が深まるにつれて、より複雑なアプリケーションを作成できるようになります。
