Java ThreadLocalの概要
6871 ワード
1.概念
synchronizedのようなスレッド同期のメカニズムは、マルチスレッドの同時問題を解決することができ、このソリューションの下で、複数のスレッドがアクセスするのは、同じ変数の内容である.マルチスレッドアクセス中に発生する同時エラーを防止するために.複数のスレッドのアクセスを同期させる必要があります.これは、複数のスレッドが変数の値に前後してアクセスまたは変更しなければならないことを意味します.これは、アクセス時間を延長してスレッドのセキュリティを交換するためのポリシーです.
ThreadLocalクラスは、スレッドごとに独自の変数コピーを維持します.各スレッドには独自の変数があり、競合条件が完全に解消されているため、これらのスレッドを同期する必要はありません.CPUによって最大限にスケジューリングされ、同時に実行されます.また、各スレッドは、その変数にアクセスする際に、読み取りと修正が独自の変数コピーであるため、変数が各アクセスのスレッドに完全に閉じ込められ、同時エラーが発生する可能性も完全に解消される.前のシナリオと比較して、これはスレッドのセキュリティを空間で交換するポリシーです.
2.例示分析
各スレッドが累積した結果は5であり,各スレッドは独自のローカル変数値を処理し,スレッド間では互いに影響を及ぼさないことが分かった.
3.ソースに深く入り込む
いったいThreadLocalクラスは、このような「スレッドごとに異なる変数コピーを提供する」ことをどのように実現しているのだろうか.まず、ThreadLocalのset()メソッドのソースコードがどのように実現されているかを見てみましょう.
ThreadLocalには内部クラスThreadLocalMapがあり、このクラスの実装はThreadLocalクラス全体のソースコードの大半を占めている.このThreadLocalMapの役割は非常に重要で、それはスレッドが本当にスレッド自身のローカル変数を保存する容器です.各スレッドには独自のThreadLocalMapインスタンスがあり、すべてのローカル変数がこのmapに保存されます.まずgetMap(Thread t)メソッドで現在のスレッドに関連するThreadLocalMapを取得し、変数の値をこのThreadLocalMapオブジェクトに設定します.もちろん、取得したThreadLocalMapオブジェクトが空の場合はcreateMapメソッドで作成します.
Threadオブジェクトには、スレッドのローカル変数をすべて保存するために使用されるThreadLocalsタイプの属性threadLocalsがあります.このプロパティは、スレッドオブジェクトの初期化時にnullです.したがって、スレッドオブジェクトに対してスレッドローカル変数を初めて使用する場合は、このthreadLocalsプロパティを初期化する必要があります.
4.まとめ
スレッド分離の秘密は、ThreadLocalMapというクラスにある.ThreadLocalMapは、キー値ペアの設定と取得(Mapオブジェクトと比較して理解)を実現するThreadLocalMapクラスの静的内部クラスであり、各スレッドには独立したThreadLocalMapコピーがあり、格納された値は、現在のスレッドによってのみ読み込まれ、変更されます.ThreadLocalクラスは、各スレッド固有のThreadLocalMapコピーを操作することで、異なるスレッドへの変数アクセスの分離を実現します.各スレッドの変数は独自なので、同時エラーはまったくありません.もう1つは、ThreadLocalMapに格納されているキーの値ペアのキーは、thisオブジェクトが指すThreadLocalオブジェクトであり、値はあなたが設定したオブジェクトです.
synchronizedのようなスレッド同期のメカニズムは、マルチスレッドの同時問題を解決することができ、このソリューションの下で、複数のスレッドがアクセスするのは、同じ変数の内容である.マルチスレッドアクセス中に発生する同時エラーを防止するために.複数のスレッドのアクセスを同期させる必要があります.これは、複数のスレッドが変数の値に前後してアクセスまたは変更しなければならないことを意味します.これは、アクセス時間を延長してスレッドのセキュリティを交換するためのポリシーです.
ThreadLocalクラスは、スレッドごとに独自の変数コピーを維持します.各スレッドには独自の変数があり、競合条件が完全に解消されているため、これらのスレッドを同期する必要はありません.CPUによって最大限にスケジューリングされ、同時に実行されます.また、各スレッドは、その変数にアクセスする際に、読み取りと修正が独自の変数コピーであるため、変数が各アクセスのスレッドに完全に閉じ込められ、同時エラーが発生する可能性も完全に解消される.前のシナリオと比較して、これはスレッドのセキュリティを空間で交換するポリシーです.
2.例示分析
6 public class ThreadLocalTest {
7
8 // Integer
9 public static final ThreadLocal<Integer> local = new ThreadLocal<Integer>() {
10 @Override
11 protected Integer initialValue() {
12 return 0;
13 }
14 };
15
16 public static void main(String[] args) throws InterruptedException {
17 Thread[] threads = new Thread[5];
18 for (int j = 0; j < 5; j++) {
19 threads[j] = new Thread(new Runnable() {
20 @Override
21 public void run() {
22 // , 5
23 int num = local.get();
24 for (int i = 0; i < 5; i++) {
25 num++;
26 }
27 //
28 local.set(num);
29 System.out.println(Thread.currentThread().getName() + " : " + local.get());
30
31 }
32 }, "Thread-" + j);
33 }
34
35 for (Thread thread : threads) {
36 thread.start();
37 }
38 }
39 }
40
41 :
42 Thread-0 : 5
43 Thread-3 : 5
44 Thread-2 : 5
45 Thread-1 : 5
46 Thread-4 : 5
各スレッドが累積した結果は5であり,各スレッドは独自のローカル変数値を処理し,スレッド間では互いに影響を及ぼさないことが分かった.
3.ソースに深く入り込む
いったいThreadLocalクラスは、このような「スレッドごとに異なる変数コピーを提供する」ことをどのように実現しているのだろうか.まず、ThreadLocalのset()メソッドのソースコードがどのように実現されているかを見てみましょう.
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
public T get() {
//
Thread t = Thread.currentThread();
// ThreadLocalMap
ThreadLocalMap map = getMap(t);
// map , ThreadLocalMap
if (map != null) {
//map ,
ThreadLocalMap.Entry e = map.getEntry(this);
// map ,
if (e != null)
return (T)e.value;
}
// map map ,
return setInitialValue();
}
private T setInitialValue() {
// ,initialValue
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
// map , ThreadLocalMap
if (map != null)
map.set(this, value);
else
// , map
createMap(t, value);
//
return value;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalには内部クラスThreadLocalMapがあり、このクラスの実装はThreadLocalクラス全体のソースコードの大半を占めている.このThreadLocalMapの役割は非常に重要で、それはスレッドが本当にスレッド自身のローカル変数を保存する容器です.各スレッドには独自のThreadLocalMapインスタンスがあり、すべてのローカル変数がこのmapに保存されます.まずgetMap(Thread t)メソッドで現在のスレッドに関連するThreadLocalMapを取得し、変数の値をこのThreadLocalMapオブジェクトに設定します.もちろん、取得したThreadLocalMapオブジェクトが空の場合はcreateMapメソッドで作成します.
// threadLocals
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
Threadオブジェクトには、スレッドのローカル変数をすべて保存するために使用されるThreadLocalsタイプの属性threadLocalsがあります.このプロパティは、スレッドオブジェクトの初期化時にnullです.したがって、スレッドオブジェクトに対してスレッドローカル変数を初めて使用する場合は、このthreadLocalsプロパティを初期化する必要があります.
4.まとめ
スレッド分離の秘密は、ThreadLocalMapというクラスにある.ThreadLocalMapは、キー値ペアの設定と取得(Mapオブジェクトと比較して理解)を実現するThreadLocalMapクラスの静的内部クラスであり、各スレッドには独立したThreadLocalMapコピーがあり、格納された値は、現在のスレッドによってのみ読み込まれ、変更されます.ThreadLocalクラスは、各スレッド固有のThreadLocalMapコピーを操作することで、異なるスレッドへの変数アクセスの分離を実現します.各スレッドの変数は独自なので、同時エラーはまったくありません.もう1つは、ThreadLocalMapに格納されているキーの値ペアのキーは、thisオブジェクトが指すThreadLocalオブジェクトであり、値はあなたが設定したオブジェクトです.