Java ThreadLocal分析
4783 ワード
参考:ThreadLocalとWeakReference
スレッド閉鎖
共有された可変データにアクセスする場合、通常は同期を使用する必要があります.同期を避ける方法の1つは、データを共有しないことです.どのようにデータが1つのスレッドにのみアクセスされるかは、同期を必要とせず、この技術はスレッド閉鎖である.
ThreadLocalはその一つの実装です.
ThreaLocalの一般的な使用
スレッドプール:JDBC接続オブジェクトはスレッドが安全であるとは限らないため、複数のスレッドが連携していない場合にグローバル変数を使用する場合、スレッドは安全ではありません.JDBCの接続をThreadLocalオブジェクトに保存することで、各スレッドに独自の接続があります.
このほかSpringなど様々なフレームワークではThreadLocal(コンテキストを保存するために使用)が多く使われている.
げんり
Thread
Threadオブジェクトに変数(フック)があり、Mapオブジェクトを保存します.�
ThreadLocalのsetメソッドを呼び出すと、Mapオブジェクトが生成され、このMapオブジェクトがThreadオブジェクトに割り当てられます.
ThreadLocal
値を設ける
値をとる
ThreadLocalは本質的に各スレッドにMapオブジェクトをバインドし、このMapオブジェクトkeyはThreadLocal自体であり、valueは設定する値である.これにより、各valueはスレッドによってのみ自分でアクセスでき、外部の他のスレッドはオブジェクトにアクセスせず、スレッドセキュリティを実現します.
ThreadとThreadLocalの関係
ThreadとThreadLocalの間には密接な関係があり、ThreadのMapはThreadLocalオブジェクト(keyとして)を持っている.
Thread
ThreadLocal
死ぬ
死ぬ
生き残る
生き残る
生き残る
死ぬ
死ぬ
生き残る
両者の間が死亡した場合、自然にGCに回収されます.両方が「生き残る」場合、GCはそれらを回収しません.では、ThreadとThreadLocalの間の「死」はどうなっているのでしょうか.
Thread死
Threadが死亡した場合、スレッド内のMapも存在しない.Mapが回収されるため、Mapに保存されているkey、valueも参照されるかどうかによって回収されるかどうかを決定し、メモリ漏洩を招くことはない.
ThreadLocal死亡
ThreadLocalが死亡した場合、スレッドは依然として生存し、Mapはまだ存在するが、MapのうちのThreadLocalをkeyとするEntryは失効し、EntryのkeyとvalueはGCされるべきである.
これを実現するために、ThreadLocalのEntryは次のように実現されます.
このように、ThreadLocalが死亡した場合、Entryではkeyが弱い参照であるため、GCはオブジェクトを回収し、メモリ漏洩を招くことはありません.
一方、Entryにおけるkey対応valueの回収は、以下の方法で実現される.
この方法でvalueをnullに設定し、次回GC時にそのオブジェクトをタイムリーに回収できるようにする.ThreadLocalが死亡した場合、メモリが漏れることはありません.
に注意
現在、多くの技術が使用されているスレッドプール技術であり、スレッドが頻繁に作成されて破棄されない場合、スレッドプールのスレッドがグローバルThreadLocalオブジェクトを取得し、大きな文字列オブジェクトを設定し、使用後にremove操作がなければ、スレッドとThreadLocalが生存しているため、その文字列オブジェクトは回収されない可能性がある.メモリの漏洩の原因となるため、使用が完了するとスレッドはThreadLocalのremoveメソッドを呼び出し、そのオブジェクトを直ちに除去し、不要なメモリ損失を防止することが望ましい.
スレッド閉鎖
共有された可変データにアクセスする場合、通常は同期を使用する必要があります.同期を避ける方法の1つは、データを共有しないことです.どのようにデータが1つのスレッドにのみアクセスされるかは、同期を必要とせず、この技術はスレッド閉鎖である.
ThreadLocalはその一つの実装です.
ThreaLocalの一般的な使用
スレッドプール:JDBC接続オブジェクトはスレッドが安全であるとは限らないため、複数のスレッドが連携していない場合にグローバル変数を使用する場合、スレッドは安全ではありません.JDBCの接続をThreadLocalオブジェクトに保存することで、各スレッドに独自の接続があります.
private static ThreadLocal connectionHolder = new
ThreadLocal() {
@Override
protected Connection initialValue() {
return DriverManager.getConnection("");
}
};
public static Condition getConnection() {
return connectionHolder.get();
}
このほかSpringなど様々なフレームワークではThreadLocal(コンテキストを保存するために使用)が多く使われている.
げんり
Thread
Threadオブジェクトに変数(フック)があり、Mapオブジェクトを保存します.�
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalのsetメソッドを呼び出すと、Mapオブジェクトが生成され、このMapオブジェクトがThreadオブジェクトに割り当てられます.
ThreadLocal
値を設ける
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value); // ThreadLocal key
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
値をとる
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
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 T initialValue() { //
return null;
}
ThreadLocalは本質的に各スレッドにMapオブジェクトをバインドし、このMapオブジェクトkeyはThreadLocal自体であり、valueは設定する値である.これにより、各valueはスレッドによってのみ自分でアクセスでき、外部の他のスレッドはオブジェクトにアクセスせず、スレッドセキュリティを実現します.
ThreadとThreadLocalの関係
ThreadとThreadLocalの間には密接な関係があり、ThreadのMapはThreadLocalオブジェクト(keyとして)を持っている.
Thread
ThreadLocal
死ぬ
死ぬ
生き残る
生き残る
生き残る
死ぬ
死ぬ
生き残る
両者の間が死亡した場合、自然にGCに回収されます.両方が「生き残る」場合、GCはそれらを回収しません.では、ThreadとThreadLocalの間の「死」はどうなっているのでしょうか.
Thread死
Threadが死亡した場合、スレッド内のMapも存在しない.Mapが回収されるため、Mapに保存されているkey、valueも参照されるかどうかによって回収されるかどうかを決定し、メモリ漏洩を招くことはない.
ThreadLocal死亡
ThreadLocalが死亡した場合、スレッドは依然として生存し、Mapはまだ存在するが、MapのうちのThreadLocalをkeyとするEntryは失効し、EntryのkeyとvalueはGCされるべきである.
これを実現するために、ThreadLocalのEntryは次のように実現されます.
static class Entry extends WeakReference> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal> k, Object v) {
super(k);
value = v;
}
}
このように、ThreadLocalが死亡した場合、Entryではkeyが弱い参照であるため、GCはオブジェクトを回収し、メモリ漏洩を招くことはありません.
一方、Entryにおけるkey対応valueの回収は、以下の方法で実現される.
private boolean cleanSomeSlots(int i, int n) {
boolean removed = false;
Entry[] tab = table;
int len = tab.length;
do {
i = nextIndex(i, len);
Entry e = tab[i];
if (e != null && e.get() == null) { // e.get() point
n = len;
removed = true;
i = expungeStaleEntry(i);
}
} while ( (n >>>= 1) != 0);
return removed;
}
expungeStaleEntry :
e.value = null; // value set null
tab[i] = null;
size--;
この方法でvalueをnullに設定し、次回GC時にそのオブジェクトをタイムリーに回収できるようにする.ThreadLocalが死亡した場合、メモリが漏れることはありません.
に注意
現在、多くの技術が使用されているスレッドプール技術であり、スレッドが頻繁に作成されて破棄されない場合、スレッドプールのスレッドがグローバルThreadLocalオブジェクトを取得し、大きな文字列オブジェクトを設定し、使用後にremove操作がなければ、スレッドとThreadLocalが生存しているため、その文字列オブジェクトは回収されない可能性がある.メモリの漏洩の原因となるため、使用が完了するとスレッドはThreadLocalのremoveメソッドを呼び出し、そのオブジェクトを直ちに除去し、不要なメモリ損失を防止することが望ましい.