#図に示すように:
#クイック スタート
次に、簡単な例を使用して、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); } }
実際、この状況は 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 サイトの他の関連記事を参照してください。