JAvaスレッドコピー--ThreadLocal


一、ThreadLocal紹介
マルチスレッド同時実行が安全でないのは、スレッドがリソースを所有していないためであり、競合するプロセスのリソースを共有しているためであり、スレッドが同時実行されるのは安全ではありません.一般的な解決策は、ロックを使用して、毎時毎刻1つのリソースが最大1つのスレッドにしか所有できないことを保証します.Javaのマルチスレッドでは、ThreadLocalはマルチスレッドプログラムの同時問題を解決するために新しい考え方を提供しています.スレッドごとにコピー変数を維持し、スレッドにプライベートなリソースを持たせることで、プロセス内のリソースを競合させる必要がなくなります.各スレッドは、他のスレッドに対応するレプリカに影響を与えることなく、独自のレプリカを独立して変更できます.まず、ThreadLocalのAPI:1、構築方法の概要ThreadLocal()を見てみましょう.スレッドのローカル変数を作成します.2、メソッド要約void set(T value):このスレッドのローカル変数の現在のスレッドコピーの値を指定値に設定します.T get():このスレッドのローカル変数の現在のスレッドコピーの値を返します.void remove():このスレッドのローカル変数の現在のスレッドの値を削除します.protected T initialValue():このスレッドのローカル変数の現在のスレッドの「初期値」を返します.protectedメソッドで、サブクラスを継承するために設計されています
例を見てみましょう.
public class MyTest {

//   ThreadLocal      static  ,        
public static ThreadLocal threadLocal = new ThreadLocal<>(); 

public static void main(String[] args) {
    Thread t1 = new Thread(new MyTask());
    Thread t2 = new Thread(new MyTask());
    Thread t3 = new Thread(new MyTask());
    //  3   
    t1.start();
    t2.start();
    t3.start();
}

}

class MyTask implements Runnable{

    @Override
    public void run() {
        MyTest.threadLocal.set("0");
        //    5 
         for(int i=1;i<5;i++){
             MyTest.threadLocal.set( MyTest.threadLocal.get().toString()+i);
         }
         System.out.println("  "+Thread.currentThread().getName()+"     :"+MyTest.threadLocal.get());
    }

}

実行結果:
スレッドThread-1の計算結果:01234スレッドThread-0の計算結果:01234スレッドThread-2の計算結果:01234
不思議な感じがしますか?3つのスレッドはいずれも同じThreadLocalの静的変数オブジェクトを使用して値を格納または取得するが,3つのスレッドの計算結果は互いに影響せず,互いに独立している.そう、これがThreadLocalの同時解決の方法であり、各スレッドに対して1つのスレッドのプライベート変数を維持し、同時にすべてのスレッドで共有することができます.
二、ThreadLocalを深く理解する
1、ThreadLocalの下位実装はMap(ThreadLocalMapという)であり、各要素のkey値はスレッドオブジェクトであり、値はスレッドの変数コピーに対応する.get、setメソッドを呼び出す場合、使用するスレッドは現在実行中のスレッドであり、現在実行中のスレッドのプライベート変数を取得、設定することができる.
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

2.get()メソッドを呼び出すときは、まず現在のスレッドを取得し、次に現在のスレッドのThreadLocalMapオブジェクトを取得し、空でない場合はThreadLocalのvalueを取り出し、そうでない場合は初期化を行い、初期化はinitialValueの値setをThreadLocalにセットする.
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}

3、set()メソッドを呼び出すときは、通常、ThreadLocalに値を設定します.get()メソッドとはあまり差がありません.
  private T setInitialValue() {  
        T value = initialValue();  
        Thread t = Thread.currentThread();  
        ThreadLocalMap map = getMap(t);  
        if (map != null)  
            map.set(this, value);  
        else  
            createMap(t, value);  
        return value;  
    }  

スレッドがアクティブであり、ThreadLocalインスタンスがアクセス可能である限り.スレッドが消失すると、スレッドのローカルインスタンスのすべてのコピーがゴミ回収されます(これらのコピーに対する他の参照がない限り).