ThreadLocalの使い方


ThreadLocalの役割:
同じスレッドにインスタンスを共有させ、異なるスレッドはそれぞれのインスタンスを使用し、互いに影響しません.
 
ThreadLocalの本格的な応用解釈:
各スレッドには独自の変数があり、Threadが終了しない限り、いつでも内部のThreadLocalMapを取り出し、ThreadLocal自身と組み合わせることで、Mapに格納されている変数を手に入れることができます.
どうしてこんなことになったの?メソッドチェーンで同じパラメータを渡す手間を解決しました!
 
適用シーン:
1つのスレッドが実行されており、多くのクラスの異なるメソッドを実行する必要があります.これらのメソッドは変数を使用しています.
では、この変数をどんどん後ろに伝えなければなりません.面倒です.
どのようにしてThreadのライフサイクル全体でこの変数を使用できるのか、ThreadLocalMapを使用して保存します.
 
各Threadには属性変数があります.
ThreadLocal.ThreadLocalMap    threadLocals
ThreadLocalでは、内部クラス(Mapとして)が定義されています.
ThreadLocalMap map
このMapは特殊です.
key    -   ThreadLocal 
value -   私たちが伝える変数
 
 
ThreadクラスのこのMapはどのように初期化されていますか?
ThreadLocalオブジェクトを作成し、getメソッドを呼び出して変数を取得すると、Threadから取得したMapが空の場合、
新しいMapが作成され、ThreadのthreadLocalsに参照が割り当てられます.
こうして、TheadにはMapの引用がある.
スレッドがまだ実行されている限り、currentThreadで独自のThreadLocalMapを得ることができます.
そして、ThreadLocalをキーとして、Mapから格納されている変数、
 
設計上、開発者にとって、ThreadとThreadLocalMapは背後に隠されています.
ThreadLocalを直接操作することで変数の格納、取得、削除を完了します!
背後に隠されているものを理解してこそ、運行原理を深く理解することができる. 
 
ソース:
ThreadクラスはThreadLocalの参照を持つ
public class Thread implements Runnable {
        ...
        ThreadLocal.ThreadLocalMap threadLocals = null;
        ...
}

  
       ThreadLocalクラスの主な方法
    public T get() {//threadLocal       Map    value
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);//map key:    this
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;// value              /  
        }
        return setInitialValue();
    }

 
現在のThreadのThreadMapセットを取得
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

 
変数をThreadMapに設定
mapが空の場合はThreadMapを初期化して保存します
最後に変数を返します
    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;
    }

 初期化変数の取得
このメソッドはprotected権限であり、ThreadLocalオブジェクトを作成するときに、このメソッドを再書き込みして初期化の目的を達成することができます.
初期化とは,変数をThreadLocalに入力し,ThreadLocalMapのvalueに格納することである.
    protected T initialValue() {
        return null;
    }

 例えば、複写初期化方法
 
ThreadLocalMapを作成し、Mapオブジェクトの参照をThreadのメンバー変数threadLocalsに渡す
これにより、ThreadLocal保存オブジェクトが実現される.
 
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

 
ThreadLocalに格納されている変数を取得します. 
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);//  Thread TheadLocalMap
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);// ThreadLocal   key  value
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

 ThreadのThreadLocalMapを入手
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

 
 
 ==================================================================================
小さな例ですが、あまりよくありません.
 
import java.text.SimpleDateFormat;
import java.util.Date;


public class DateUtil {
	private static final String DATA_FORMAT = "yyyy-MM-dd HH:mm:ss";
	
	private static ThreadLocal threadLocal = new ThreadLocal() {
		//   :      initialValue(),           
		// currentThread key  ThreadLocalMap
		//    ,   Map。key ThreadLocal  ,value          
		//  Map     Thread  ThreadLocalMap【      currentThread    Map ,   Map       】
		
		@Override
		protected  SimpleDateFormat initialValue() {
			return new SimpleDateFormat(DATA_FORMAT);
		}
	};
	
	public static SimpleDateFormat getDateFormat() {
		//   :threadLocal    get()
		//get()   getMap(t),t currentThread,   Thread     threadLocals 
		//  ThreadLocal.ThreadLocalMap threadLocals = null;
		//     ThreadLocalMap
		//  ,   initialValue()     ,        ,      null
		return threadLocal.get();
	}
	
	public static String format(Date date) {
		return getDateFormat().format(date);
	}
	
	public static void main(String[] args) {
		final Date date = new Date();
		for(short i=0; i<10000; i++) 
			//   :  Thread     t
			new Thread(new Runnable() {
				@Override
				public void run() {
					System.out.println(DateUtil.getDateFormat().format(date));
				}
			}).start();
	}
}