ThreadLocal を使用すると、スレッド プライベート変数を作成できます。この変数は他のスレッドには表示されません。ThreadLocal は、各スレッドに変数のコピーを作成します。各スレッドは、独自のプライベート スレッドにアクセスできます。コード例は次のとおりです:
public class ThreadLocalDemo { //创建一个ThreadLocal对象,用来为每个线程会复制保存一份变量,实现线程封闭 private static ThreadLocal<Integer> localNum = new ThreadLocal<Integer>(){ @Override protected Integer initialValue() { return 1; } }; public static void main(String[] args) { //线程0 new Thread(){ @Override public void run() { localNum.set(1); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } localNum.set(localNum.get()+10); System.out.println(Thread.currentThread().getName()+":"+localNum.get());//11 } }.start(); //线程1 new Thread(){ @Override public void run() { localNum.set(3); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } localNum.set(localNum.get()+20); System.out.println(Thread.currentThread().getName()+":"+localNum.get());//23 } }.start(); System.out.println(Thread.currentThread().getName()+":"+localNum.get());//0 } }
上で述べたように、メイン スレッドと新しく作成された 2 つのスレッドを数えると、合計 3 つのスレッドがあります。各スレッドには独自のプライベート スレッド変数が含まれています。ここでは値 1 を設定します。値の設定と取得には set() メソッドと get() メソッドが使用されます。実行結果は次のとおりです。 Analysis
ThreadLocal は、任意のタイプのオブジェクトを受け入れることができるジェネリック クラスです。ThreadLocalMap の静的な内部クラスを維持します。実際に使用する get()、set() などは、このクラスから来ており、現在のスレッドは、プライベート値を記録するために ThreadLocalMap オブジェクトを作成します
最初に set() メソッドを確認します
public void set(T value) { //拿到当前线程 Thread t = Thread.currentThread(); //拿到当前线程map ThreadLocalMap map = getMap(t); if (map != null) //存在设置值 map.set(this, value); else //不存在则创建 createMap(t, value); }
void createMap(Thread t, T firstValue) { //threadLocals属性即为此map t.threadLocals = new ThreadLocalMap(this, firstValue); }
次に get() メソッド
public T get() { //拿到当前线程 Thread t = Thread.currentThread(); //拿到当前线程对应的map ThreadLocalMap map = getMap(t); //如果已有map if (map != null) { //取值操作, 拿到对应的Entry ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } //没有map, 则去创建初始化一个map return setInitialValue(); }
private T setInitialValue() { //initialValue()方法返回的value为null T value = initialValue(); //拿到当前线程去创建对应的map Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }ThreadLocal は ThreadLocalMap のカプセル化として理解できますThreadLocal メモリ リークの問題ThreadLocalMap では、ThreadLocal オブジェクトはキーとして弱い参照を使用します
この場合、ThreadLocal に外部強参照がない場合、キーは GC によってリサイクルされることになるため、ThreadLocalMap 内のキーは null になり、値には依然として強参照チェーン
スレッドは同時に複数の ThreadLocal を使用できます。弱参照としてキーがリサイクルされると、値はリサイクルできなくなり、この ThreadLocal のライフ サイクルが長くなります。このスレッドのように (スレッドの実行後のこの値の強参照チェーンが中断されるため)、スレッドが終了せず、蓄積された値をリサイクルできない場合、メモリ リークの問題が発生します。
ここでの問題を解決する方法は次のとおりです。使用後は毎回 ThreadLocal を呼び出します。remove() メソッドはデータをクリアしますpublic void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }ここでは、強参照としてキーの違いを見ていきますIf キー強参照として使用される場合、そのライフサイクルはスレッドと同じ長さであり、安定した強参照チェーンが存在します。強参照チェーンはリサイクルできないため、メモリ リークが発生します。弱参照として使用される場合、GC はそれらを自動的にリサイクルします。値は後続のremove()メソッドでより適切にリサイクルできるため、通常はThreadLocalを使用します。プライベート静的になるように設計されており、使用後に手動で削除するにはremove()メソッドを使用します
以上がJava における ThreadLocal スレッド変数の実装原理は何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。