いつThreadLocal
変数を使用すればよいですか?
使い方は?
RequestUtils.getCurrentRequestThreadLocal()
。ただし、これは非常に洗練されているとは言えませんが、これは、ほとんどの場合、ThreadLocal自体があまり洗練されていないためです。
いつThreadLocal
変数を使用すればよいですか?
使い方は?
RequestUtils.getCurrentRequestThreadLocal()
。ただし、これは非常に洗練されているとは言えませんが、これは、ほとんどの場合、ThreadLocal自体があまり洗練されていないためです。
回答:
考えられる(そして一般的な)使用法の1つは、スレッドセーフではないオブジェクトがあるが、そのオブジェクトへのアクセスの同期を避けたい場合です(私はSimpleDateFormatを調べています)。代わりに、各スレッドにオブジェクトの独自のインスタンスを割り当てます。
例えば:
public class Foo
{
// SimpleDateFormat is not thread-safe, so give one to each thread
private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("yyyyMMdd HHmm");
}
};
public String formatIt(Date date)
{
return formatter.get().format(date);
}
}
SimpleDateFormat
です。おそらく、スレッドセーフな代替手段を使用する方が良いでしょう。あなたがその同意する場合はシングルトンが悪い、その後ThreadLocal
さらに悪くなります。
a ThreadLocal
は指定された内のデータへの参照であるため、スレッドプールを使用するアプリケーションサーバーでをThread
使用すると、クラスローディングリークが発生する可能性がありThreadLocal
ます。のクリーンアップやのメソッドの使用については、十分に注意する必要がありThreadLocal
ます。get()
set()
ThreadLocal
remove()
完了時にクリーンアップしないと、デプロイされたWebアプリケーションの一部としてロードされたクラスへの参照が永続ヒープに残り、ガベージコレクションは行われません。webappが所有するものではないThread
ため、webappを再デプロイ/アンデプロイしても、webappのクラスへの各の参照はクリーンアップされThread
ません。連続するデプロイメントごとに、クラスの新しいインスタンスが作成され、ガベージコレクションは行われません。
java.lang.OutOfMemoryError: PermGen space
いくつかのグーグルが原因でメモリ不足の例外が発生し-XX:MaxPermSize
、バグを修正する代わりにおそらく増加するだけです。
これらの問題が発生した場合は、EclipseのMemory Analyzerを使用するか、Frank Kievietのガイドとフォローアップに従って、どのスレッドとクラスがこれらの参照を保持しているかを判断できます。
更新:私が抱えていたいくつかの問題を追跡するのに役立つAlex Vasseurのブログエントリを再発見しましたThreadLocal
。
多くのフレームワークはThreadLocalsを使用して、現在のスレッドに関連するいくつかのコンテキストを維持しています。たとえば、現在のトランザクションがThreadLocalに格納されている場合、スタックの下位の誰かがアクセスする必要がある場合に備えて、すべてのメソッド呼び出しを通じてパラメーターとして渡す必要はありません。Webアプリケーションは、現在の要求とセッションに関する情報をThreadLocalに保存し、アプリケーションがそれらに簡単にアクセスできるようにします。Guiceを使用すると、注入されたオブジェクトのカスタムスコープ(Guiceのデフォルトのサーブレットスコープ)を実装するときにThreadLocalsを使用できますもおそらくそれらを使用します)。
ThreadLocalsは一種のグローバル変数です(ただし、1つのスレッドに制限されているため、害は少し少なくなります)。不要な副作用やメモリリークを回避するために、これらを使用する場合は注意が必要です。ThreadLocal値が不要になったときに常に自動的にクリアされ、APIの不正な使用が不可能になるように(たとえば、このように)APIを設計します。ThreadLocalsは、コードクリーナーを作るために使用することができ、そしていくつかのまれなケースでは、彼らは何かを動作させるための唯一の方法です(私の現在のプロジェクトは、2つのこのような例があった。それらは文書化され、ここで、「静的フィールドとグローバル変数」の下)。
Javaでは、スレッドごとに異なるデータムがある場合、そのデータムを必要とする(または必要とする可能性がある)すべてのメソッドに渡すか、データムをスレッドに関連付けるかを選択できます。すべてのメソッドが共通の「コンテキスト」変数を渡す必要がある場合は、どこにでもデータを渡すことができます。
そうでない場合は、追加のパラメーターを使用してメソッドシグネチャを混乱させたくない場合があります。スレッド化されていない世界では、Javaのグローバル変数に相当する問題を解決できます。スレッド化された単語では、グローバル変数に相当するものはスレッドローカル変数です。
'datum' is the singular form and 'data' is the plural form.
本のJava Concurrency in Practiceには非常に良い例があります。著者(Joshua Bloch)は、スレッドの制限がスレッドの安全性とThreadLocalを実現する最も簡単な方法の1つであると説明しています。がスレッドの制限を維持するためのより正式な方法であると説明しています。最後に、彼はそれをグローバル変数として使用することによって人々がどのようにそれを悪用することができるかについても説明します。
言及した本からテキストをコピーしましたが、ThreadLocalをどこで使用するかを理解することはそれほど重要ではないため、コード3.10が欠落しています。
スレッドローカル変数は、変更可能なシングルトンまたはグローバル変数に基づく設計での共有を防ぐためによく使用されます。たとえば、シングルスレッドアプリケーションは、すべてのメソッドにConnectionを渡さなくても済むように、起動時に初期化されるグローバルデータベース接続を維持する場合があります。JDBC接続はスレッドセーフではない可能性があるため、追加の調整なしでグローバル接続を使用するマルチスレッドアプリケーションもスレッドセーフではありません。ThreadLocalを使用してJDBC接続を格納することにより、リスト3.10のConnectionHolderのように、各スレッドは独自の接続を持ちます。
ThreadLocalは、アプリケーションフレームワークの実装で広く使用されています。たとえば、J2EEコンテナは、EJB呼び出しの間、トランザクションコンテキストを実行中のスレッドに関連付けます。これは、トランザクションコンテキストを保持する静的Thread-Localを使用して簡単に実装されます。フレームワークコードが現在実行中のトランザクションを判別する必要がある場合、このThreadLocalからトランザクションコンテキストをフェッチします。これは、すべてのメソッドに実行コンテキスト情報を渡す必要性を減らすという点で便利ですが、このメカニズムを使用するすべてのコードをフレームワークに結合します。
ThreadLocalのスレッド制限プロパティをグローバル変数を使用するライセンスとして、または「隠された」メソッド引数を作成する手段として扱うことにより、ThreadLocalを悪用するのは簡単です。グローバル変数と同様に、スレッドローカル変数は再利用性を損ない、クラス間に隠された結合を導入する可能性があるため、注意して使用する必要があります。
基本的に、あなたが必要とするとき、現在のスレッドに依存する変数の値を、それはあなたには、いくつかの他の方法でスレッドに値を添付するために便利ではありません(たとえば、スレッドのサブクラス)。
典型的なケースは、他のフレームワークがスレッドを作成した場合です、サーブレットコンテナーなど、コードが実行を、または変数が「変数ではなく論理的な場所」にあるため、ThreadLocalを使用するほうが理にかなっている場合です。 Threadサブクラスまたは他のハッシュマップにぶら下がっている)。
私のWebサイトで、さらに興味深いのは、ThreadLocalをいつ使用するかについてのいくつかの議論と例です。
一部の人々は、スレッド番号が必要な特定の並行アルゴリズムで各スレッドに「スレッドID」を添付する方法としてThreadLocalを使用することを推奨しています(例:Herlihy&Shavitを参照)。そのような場合は、本当に利益を得ているか確認してください!
JavaのThreadLocalはJDK 1.2で導入されましたが、後でThreadLocal変数の型安全性を導入するためにJDK 1.5で生成されました。
ThreadLocalはThreadスコープに関連付けることができます。Threadによって実行されるすべてのコードはThreadLocal変数にアクセスできますが、2つのスレッドはお互いにThreadLocal変数を参照できません。
各スレッドはThreadLocal変数の排他的コピーを保持します。これは、スレッドが終了または終了した後、通常または何らかの例外が原因でガベージコレクションに適格になります。これらのThreadLocal変数には他のライブ参照がありません。
JavaのThreadLocal変数は通常、クラスのプライベート静的フィールドであり、スレッド内でその状態を維持します。
ドキュメントはそれを非常によく言っています:「(スレッドローカル変数]にアクセスする各スレッド(そのgetメソッドまたはsetメソッドを介して)は、独自の独立して初期化された変数のコピーを持っています」。
各スレッドが何かの独自のコピーを持つ必要がある場合に使用します。デフォルトでは、データはスレッド間で共有されます。
threadlocal変数を使用できる2つの使用例
-1-状態をスレッドに関連付ける必要がある場合(たとえば、ユーザーIDまたはトランザクションID)。これは通常、サーブレットに送信されるすべてのリクエストに一意のトランザクションIDが関連付けられているWebアプリケーションで発生します。
// This class will provide a thread local variable which
// will provide a unique ID for each thread
class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId =
ThreadLocal.<Integer>withInitial(()-> {return nextId.getAndIncrement();});
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
ここでメソッドwithInitialはラムダ式を使用して実装されていることに注意してください。
2-もう1つの使用例は、スレッドセーフなインスタンスが必要で、同期を使用したくない場合です。同期によるパフォーマンスコストが高くなるためです。そのようなケースの1つは、SimpleDateFormatが使用される場合です。SimpleDateFormatはスレッドセーフではないため、スレッドセーフにするメカニズムを提供する必要があります。
public class ThreadLocalDemo1 implements Runnable {
// threadlocal variable is created
private static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue(){
System.out.println("Initializing SimpleDateFormat for - " + Thread.currentThread().getName() );
return new SimpleDateFormat("dd/MM/yyyy");
}
};
public static void main(String[] args) {
ThreadLocalDemo1 td = new ThreadLocalDemo1();
// Two threads are created
Thread t1 = new Thread(td, "Thread-1");
Thread t2 = new Thread(td, "Thread-2");
t1.start();
t2.start();
}
@Override
public void run() {
System.out.println("Thread run execution started for " + Thread.currentThread().getName());
System.out.println("Date formatter pattern is " + dateFormat.get().toPattern());
System.out.println("Formatted date is " + dateFormat.get().format(new Date()));
}
}
Java 8リリース以降、初期化する宣言的な方法がさらにありますThreadLocal
。
ThreadLocal<Cipher> local = ThreadLocal.withInitial(() -> "init value");
Java 8がリリースされるまで、次のことを行う必要がありました。
ThreadLocal<String> local = new ThreadLocal<String>(){
@Override
protected String initialValue() {
return "init value";
}
};
さらに、使用されるクラスのインスタンス化メソッド(コンストラクター、ファクトリーメソッド)がThreadLocal
パラメーターを取らない場合は、メソッド参照(Java 8で導入)を使用するだけで済みます。
class NotThreadSafe {
// no parameters
public NotThreadSafe(){}
}
ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);
注:が呼び出されるjava.util.function.Supplier
ときにのみ評価されるラムダを
渡しているがThreadLocal#get
、値が以前に評価されていないため、評価は遅延です。
ThreadLocalパターンには十分注意する必要があります。Philのようにいくつかの主要な欠点がありますが、言及されなかったのは、ThreadLocalコンテキストを設定するコードが「再入可能」でないことを確認することです。
予期しないときにスレッドの情報が変化し始める可能性があるため、情報を設定するコードが2回または3回実行されると、悪いことが発生する可能性があります。したがって、再設定する前に、ThreadLocal情報が設定されていないことを確認してください。
ThreadLocal
パターンだけでなく、あなたが書くすべてのコードに当てはまります。その場合はF(){ member=random(); F2(); write(member); }
、新しい値を持つF2オーバーライドメンバー、そして明らかにwrite(member)
、もはやあなたがしている数書きませんrandom()
編を。これは文字通り単なる常識です。同様に、あなたがそうするならF(){ F(); }
、あなたの無限ループで幸運を祈ります!これはどこにでも当てはまり、に限定されませんThreadLocal
。
いつ?
オブジェクトがスレッドセーフでない場合は、スケーラビリティを妨げる同期の代わりに、1つのオブジェクトをすべてのスレッドに割り当て、スレッドのスコープであるThreadLocalを維持します。スレッドセーフではないが最もよく使用されるオブジェクトの1つは、データベース接続とJMS接続です。
どうやって ?
1つの例は、SpringフレームワークがThreadLocalを頻繁に使用して、これらの接続オブジェクトをThreadLocal変数に保持することにより、背後でトランザクションを管理することです。高レベルでは、トランザクションが開始されると、接続が取得され(自動コミットが無効になります)、それがThreadLocalに保持されます。以降のdb呼び出しでは、同じ接続を使用してdbと通信します。最後に、ThreadLocalから接続を取得し、トランザクションをコミット(またはロールバック)して、接続を解放します。
log4jもMDCを維持するためにThreadLocalを使用すると思います。
ThreadLocal
異なるスレッド間で共有すべきではないが、存続期間中は各スレッドからアクセスできる状態にしたい場合に便利です。
例として、各リクエストが異なるスレッドによって処理されるWebアプリケーションを想像してください。リクエストごとに複数のデータが必要になると想像してください。これは計算に非常にコストがかかります。ただし、そのデータは着信要求ごとに変更されている可能性があります。つまり、プレーンキャッシュを使用できません。この問題の簡単で迅速な解決策は、ThreadLocal
このデータへのアクセスを保持する変数を用意することです。これにより、要求ごとに1回だけ計算する必要があります。もちろん、この問題はを使用しなくても解決できますがThreadLocal
、説明のために考案しました。
とは言っても、ThreadLocal
sは本質的にグローバルな状態の一種であることを覚えておいてください。その結果、他の多くの影響があり、他のすべての可能なソリューションを検討した後でのみ使用する必要があります。
ThreadLocal
、オブジェクトフィールドとして使用できます...
ThreadLocal
世界中でアクセス可能なのいくつかの誤用に遭遇した可能性があります。あなたが言ったように、それはまだ可視性を制限するクラスフィールドとして使用できます。
ここでは特に新しいことはありませんが、今日ThreadLocal
、WebアプリケーションでBean Validationを使用するときに非常に役立つことがわかりました。検証メッセージはローカライズされていますが、デフォルトではを使用しますLocale.getDefault()
。をValidator
別のMessageInterpolator
で構成できますが、Locale
呼び出し時にを指定する方法はありませんvalidate
。そのため、静的ThreadLocal<Locale>
(または、より良いのは、必要になる可能性のある他のものを備えた一般的なコンテナー)をThreadLocal
作成しMessageInterpolator
、Locale
そこからカスタムを選択するようにすることです。次のステップはServletFilter
、セッション値を使用するを書き込むかrequest.getLocale()
、ロケールを選択して保存することですThreadLocal
参考にしてください。
@unknown(google)で述べたように、その使用法は、参照される値が各スレッドで一意であるグローバル変数を定義することです。これは通常、現在の実行スレッドにリンクされているある種のコンテキスト情報を格納することを伴います。
これをJava EE環境で使用して、Java EE対応でないクラスにユーザーIDを渡します(HttpSessionまたはEJB SessionContextにアクセスできません)。このように、セキュリティベースの操作にIDを使用するコードは、すべてのメソッド呼び出しで明示的に渡す必要なく、どこからでもIDにアクセスできます。
ほとんどのJava EE呼び出しでの操作の要求/応答サイクルは、ThreadLocalを設定および設定解除するための明確に定義された入口および出口ポイントを提供するため、このタイプの使用法を簡単にします。
ThreadLocalは、非同期メソッドの複数のスレッドによる可変オブジェクトへのアクセスが確実に同期されるようにします。つまり、可変オブジェクトをメソッド内で不変にします。
これは、スレッドごとにアクセス可能な可変オブジェクトの新しいインスタンスを与えることによって実現されます。つまり、各スレッドへのローカルコピーです。これは、ローカル変数のようにアクセスされるメソッドのインスタンス変数を作成するためのハックです。ご存じのとおり、メソッドのローカル変数はスレッドでのみ使用できますが、1つの違いがあります。メソッドローカル変数は、メソッドの実行が終了するとスレッドで使用できなくなります。スレッドローカルで共有される可変オブジェクトは、クリーンアップするまで複数のメソッドで使用できます。
定義により:
JavaのThreadLocalクラスを使用すると、同じスレッドでのみ読み書きできる変数を作成できます。したがって、2つのスレッドが同じコードを実行していて、コードにThreadLocal変数への参照がある場合でも、2つのスレッドは互いのThreadLocal変数を参照できません。
Thread
Javaのそれぞれに含まThreadLocalMap
れています。
どこ
Key = One ThreadLocal object shared across threads.
value = Mutable object which has to be used synchronously, this will be instantiated for each thread.
ThreadLocalの達成:
次に、以下のような(の有無にかかわらずinitialValue()
)可変オブジェクトを保持するThreadLocalのラッパークラスを作成します。
このラッパーのゲッターとセッターは、可変オブジェクトではなくスレッドローカルインスタンスで動作します。
threadlocalのgetter()がのthreadlocalmapで値を検出しなかった場合Thread
。次に、initialValue()を呼び出して、スレッドに関するプライベートコピーを取得します。
class SimpleDateFormatInstancePerThread {
private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd") {
UUID id = UUID.randomUUID();
@Override
public String toString() {
return id.toString();
};
};
System.out.println("Creating SimpleDateFormat instance " + dateFormat +" for Thread : " + Thread.currentThread().getName());
return dateFormat;
}
};
/*
* Every time there is a call for DateFormat, ThreadLocal will return calling
* Thread's copy of SimpleDateFormat
*/
public static DateFormat getDateFormatter() {
return dateFormatHolder.get();
}
public static void cleanup() {
dateFormatHolder.remove();
}
}
今wrapper.getDateFormatter()
呼ぶthreadlocal.get()
と、それはチェックしますcurrentThread.threadLocalMap
が含まれ、この(ThreadLocalの)インスタンスを。
はいの場合、対応するスレッドローカルインスタンスの値(SimpleDateFormat)を返します。
それ以外の場合は、このスレッドローカルインスタンス、initialValue()でマップを追加します。
これにより、この変更可能なクラスでスレッドセーフが達成されます。各スレッドによって、独自の変更可能なインスタンスを使用していますが、同じThreadLocalインスタンスを使用しています。すべてのスレッドは同じThreadLocalインスタンスをキーとして共有しますが、値として異なるSimpleDateFormatインスタンスを共有します。
https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java
スレッドローカル変数は、変更可能なシングルトンまたはグローバル変数に基づく設計での共有を防ぐためによく使用されます。
これは、接続プールを使用していないときに、スレッドごとに個別のJDBC接続を作成するようなシナリオで使用できます。
private static ThreadLocal<Connection> connectionHolder
= new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
getConnectionを呼び出すと、そのスレッドに関連付けられた接続が返されます。dateformatやスレッド間で共有したくないトランザクションコンテキストなどの他のプロパティでも同じことができます。
同じためにローカル変数を使用することもできますが、これらのリソースは通常、作成に時間がかかるため、それらを使用してビジネスロジックを実行するたびに何度も作成する必要はありません。ただし、ThreadLocal値はスレッドオブジェクト自体に格納され、スレッドがガベージコレクションされるとすぐに、これらの値も失われます。
このリンクは、ThreadLocalの使い方を非常によく説明しています。
使用するための3つのシナリオがあり、クラスのヘルパーを最良のものが使用されるマルチスレッドコードでのSimpleDateFormat、同じようにThreadLocalの
シナリオ
1-アプリを遅くするロックまたは同期メカニズムの助けを借りて、同様の共有オブジェクトを使用する
2-メソッド内でローカルオブジェクトとして使用する
このシナリオでは、4つのスレッドがあり、それぞれがメソッドを1000回呼び出す場合、
4000の SimpleDateFormat オブジェクトが作成され、GCがそれらを消去するのを待機しています。
3- ThreadLocalの使用
私たちは4スレッドを持っている場合、我々はに与えた各スレッド1つのSimpleDateFormatのインスタンスを
私たちは持っているので、4つのスレッド、4つのオブジェクトのSimpleDateFormatのを。
ロックメカニズムやオブジェクトの作成と破棄は必要ありません。(良い時間の複雑さと空間の複雑さ)
ThreadLocalは、JVMによって特別にプロビジョニングされる機能であり、スレッドのみに分離されたストレージスペースを提供します。インスタンススコープ変数の値のように、クラスの特定のインスタンスのみにバインドされます。各オブジェクトには唯一の値があり、他の各値を見ることができません。ThreadLocal変数の概念もそうです。それらは、オブジェクトインスタンスの意味でスレッドに対してローカルです。それを作成したスレッドを除いて、他のスレッドはそれを見ることができません。こちらをご覧ください
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
public class ThreadId {
private static final AtomicInteger nextId = new AtomicInteger(1000);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement());
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
public static void main(String[] args) {
new Thread(() -> IntStream.range(1, 3).forEach(i -> {
System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
})).start();
new Thread(() -> IntStream.range(1, 3).forEach(i -> {
System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
})).start();
new Thread(() -> IntStream.range(1, 3).forEach(i -> {
System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
})).start();
}
}
Threadlocalは、ゼロのコストでオブジェクトの再利用を実現する非常に簡単な方法を提供します。
各更新通知で、複数のスレッドが可変キャッシュのイメージを作成している状況がありました。
各スレッドでThreadlocalを使用したので、各スレッドは古いイメージをリセットし、各更新通知でキャッシュから再度更新するだけで済みます。
オブジェクトプールからの通常の再利用可能なオブジェクトには、スレッドセーフのコストが関連付けられていますが、このアプローチにはありません。
次の小さな例を試して、ThreadLocal変数の感触をつかんでください。
public class Book implements Runnable {
private static final ThreadLocal<List<String>> WORDS = ThreadLocal.withInitial(ArrayList::new);
private final String bookName; // It is also the thread's name
private final List<String> words;
public Book(String bookName, List<String> words) {
this.bookName = bookName;
this.words = Collections.unmodifiableList(words);
}
public void run() {
WORDS.get().addAll(words);
System.out.printf("Result %s: '%s'.%n", bookName, String.join(", ", WORDS.get()));
}
public static void main(String[] args) {
Thread t1 = new Thread(new Book("BookA", Arrays.asList("wordA1", "wordA2", "wordA3")));
Thread t2 = new Thread(new Book("BookB", Arrays.asList("wordB1", "wordB2")));
t1.start();
t2.start();
}
}
スレッドBookAが最初に実行された場合のコンソール出力:
結果BookA: 'wordA1、wordA2、wordA3'。
結果BookB: 'wordB1、wordB2'。
スレッドBookBが最初に実行された場合のコンソール出力:
結果BookB: 'wordB1、wordB2'。
結果BookA: 'wordA1、wordA2、wordA3'。