ホームページ >Java >&#&チュートリアル >Java ThreadLocalクラスの使い方
#図に示すように:
#クイック スタート
次に、簡単な例を使用して、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 の原理
この図からわかるように、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; }
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(); }
##
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); } }summary各スレッド内には、threadLocals という名前のメンバー変数があります。この変数の型はハッシュ マップです。ここで、キーはは定義した ThreadLocal 変数の this 参照であり、value は set メソッドを使用して設定した値です。各スレッドのローカル変数は、スレッド自身のメモリ変数 threadLocals に格納されます。現在のスレッドが終了しない場合、これらのローカル変数は常に存在するため、メモリ オーバーフローが発生する可能性があります。したがって、削除後に ThreadLocal の Remove メソッドを呼び出すことを忘れないでください。スレッドに対応する threadLocals のローカル変数。 ThreadLocal メモリ リークメモリ リークはなぜ発生するのでしょうか? ThreadLocalMap は、ThreadLocal の弱参照をキーとして使用します。ThreadLocal がそれを参照するための外部強参照を持たない場合、ThreadLocal はシステム GC 中に必然的にリサイクルされます。 、ThreadLocalMap は、null キーを持つエントリが表示された場合、
実際、この状況は ThreadLocalMap の設計で考慮されており、いくつかの保護措置が追加されています。スレッド ThreadLocalMap 内の null であるすべてのキーは、get()、set()、および Remove の実行中にクリアされます。 ThreadLocal.値の()。ただし、これらの受動的な予防策では、メモリ リークが発生しないことを保証できません。静的 ThreadLocal を使用すると、ThreadLocal のライフ サイクルが延長され、メモリ リークが発生する可能性があります。
割り当てでは ThreadLocal が使用され、メモリ リークにつながる get()、set()、remove() メソッドは呼び出されなくなりました。
弱参照を使用する理由
強参照を使用する場合: ThreadLocalMap のライフ サイクルは基本的に Thread のライフ サイクルと同じであることがわかっています。現在のスレッドが終了しない場合、ThreadLocalMap は GC によってリサイクルされません。と ThreadLocalMap が ThreadLocal の権利を保持します。強い参照を行うと、ThreadLocal はリサイクルされません。スレッドのライフサイクルが長い場合、手動で削除しないと kv が蓄積され、OOM
remove()
メソッドを呼び出します。以上がJava ThreadLocalクラスの使い方の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。