ThreadLocal
いつ変数を使用する必要がありますか?
どのように使用されますか?
ThreadLocal
いつ変数を使用する必要がありますか?
どのように使用されますか?
aThreadLocal
は特定の 内のデータへの参照であるため、スレッド プールを使用するアプリケーション サーバーで s をThread
使用すると、クラスローディング リークが発生する可能性があります。s をクリーンアップしたり、 'sメソッドを使用したりする場合は、細心の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 を使用すると、注入されたオブジェクトのカスタム スコープを実装するときに ThreadLocals を使用できます (Guice のデフォルトのサーブレット スコープもおそらくそれらを使用します)。
ThreadLocals は一種のグローバル変数です (ただし、1 つのスレッドに制限されているため、多少害は少なくなります)、望ましくない副作用やメモリ リークを避けるために、これらを使用するときは注意が必要です。ThreadLocal 値が不要になったときに常に自動的にクリアされ、API の誤った使用が不可能になるように API を設計します (たとえば、この のように)。ThreadLocals を使用してコードをよりクリーンにすることができ、まれに何かを機能させる唯一の方法です (私の現在のプロジェクトにはそのようなケースが 2 つあります。それらは「静的フィールドとグローバル変数」の下に記載されています)。
本Java Concurrency in Practiceに非常に良い例があります。著者 ( Joshua Bloch ) は、スレッドの制限がスレッドの安全性を達成する最も簡単な方法の 1 つであり、ThreadLocalがスレッドの制限を維持するためのより正式な手段であると説明しています。最後に、彼はそれをグローバル変数として使用することで、人々がそれをどのように悪用できるかについても説明しています。
上記の本からテキストをコピーしましたが、ThreadLocal をどこで使用する必要があるかを理解することはそれほど重要ではないため、コード 3.10 が欠落しています。
スレッドローカル変数は、変更可能なシングルトンまたはグローバル変数に基づく設計での共有を防ぐためによく使用されます。たとえば、シングルスレッド アプリケーションは、すべてのメソッドに Connection を渡す必要がないように、起動時に初期化されるグローバル データベース接続を維持する場合があります。JDBC 接続はスレッド セーフではない可能性があるため、追加の調整なしでグローバル接続を使用するマルチスレッド アプリケーションもスレッド セーフではありません。リスト 3.10 の ConnectionHolder のように、ThreadLocal を使用して JDBC 接続を格納すると、各スレッドは独自の接続を持つようになります。
ThreadLocal は、アプリケーション フレームワークの実装に広く使用されています。たとえば、J2EE コンテナーは、EJB 呼び出しの間、トランザクション コンテキストを実行中のスレッドに関連付けます。これは、トランザクション コンテキストを保持する静的な Thread-Local を使用して簡単に実装できます。フレームワーク コードが現在実行中のトランザクションを特定する必要がある場合、この ThreadLocal からトランザクション コンテキストをフェッチします。これは、実行コンテキスト情報をすべてのメソッドに渡す必要性を減らすという点で便利ですが、このメカニズムを使用するすべてのコードをフレームワークに結合します。
ThreadLocal のスレッド限定プロパティを、グローバル変数を使用するためのライセンスとして、または「隠し」メソッド引数を作成する手段として扱うことで、簡単に悪用できます。グローバル変数と同様に、スレッドローカル変数は再利用性を損なう可能性があり、クラス間の隠れた結合を導入する可能性があるため、注意して使用する必要があります。
基本的に、変数の値を現在のスレッドに依存させる必要があり、その値を別の方法(スレッドのサブクラス化など) でスレッドにアタッチするのは都合が悪い場合です。
典型的なケースは、コードが実行されているスレッド (サーブレット コンテナーなど) が他のフレームワークによって作成されている場合、または変数が (変数ではなく) 「その論理的な場所にある」ため、ThreadLocal を使用する方が理にかなっている場合です。 Thread サブクラスまたは他のハッシュ マップからぶら下がっている)。
私の Web サイトには、ThreadLocal をいつ使用するかについての詳細な説明と例があり、これも興味深いものです。
一部の人々は、スレッド番号が必要な特定の同時実行アルゴリズムで各スレッドに「スレッド ID」を付加する方法として ThreadLocal を使用することを提唱しています (Herlihy & Shavit などを参照)。そんな時は、本当にお得になっているかチェック!
Java の ThreadLocal は JDK 1.2 で導入されましたが、後に JDK 1.5 で一般化され、ThreadLocal 変数にタイプ セーフが導入されました。
ThreadLocal は Thread スコープに関連付けることができます。Thread によって実行されるすべてのコードは ThreadLocal 変数にアクセスできますが、2 つのスレッドは互いの ThreadLocal 変数を参照できません。
各スレッドは、ThreadLocal 変数の排他的なコピーを保持します。これらの ThreadLocal 変数が他のライブ参照を持たない場合、スレッドが終了または終了した後、通常または何らかの例外が原因で、ガベージ コレクションの対象となります。
Java の ThreadLocal 変数は通常、クラスのプライベートな静的フィールドであり、スレッド内でその状態を維持します。
ドキュメントには、「[スレッドローカル変数] に (get メソッドまたは set メソッドを介して) アクセスする各スレッドには、独自の、独立して初期化された変数のコピーがある」という記述があります。
各スレッドが何かの独自のコピーを持つ必要がある場合に使用します。デフォルトでは、データはスレッド間で共有されます。
スレッドローカル変数を使用できる 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 つは、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()));
}
}
ThreadLocalパターンには十分注意する必要があります。Philが言及したようないくつかの大きな欠点がありますが、言及されていないのは、ThreadLocalコンテキストを設定するコードが「再入可能」でないことを確認することです。
情報を設定するコードが2回目または3回実行されると、予期しないときにスレッド上の情報が変化し始める可能性があるため、悪いことが起こる可能性があります。したがって、再度設定する前に、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 の達成:
次に、以下のように変更可能なオブジェクトを保持する ThreadLocal のラッパー クラスを作成します ( の有無にかかわらずinitialValue()
)。
このラッパーのゲッターとセッターは、変更可能なオブジェクトではなく、スレッドローカル インスタンスで動作するようになりました。
Thread
threadlocal の getter() が;の threadlocalmap で値を見つけられなかった場合。次に、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()
ていることを確認します。
はいの場合は、対応するスレッドローカル インスタンスの値 (SimpleDateFormat) を返します。
それ以外の場合は、このスレッドローカル インスタンスの initialValue() を使用してマップを追加します。currentThread.threadLocalMap
これにより、この変更可能なクラスでスレッド セーフが達成されます。各スレッドは、独自の可変インスタンスで動作しますが、同じ ThreadLocal インスタンスで動作します。つまり、すべてのスレッドが同じ ThreadLocal インスタンスをキーとして共有しますが、異なる SimpleDateFormat インスタンスを値として共有します。
https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java
@unknown (google) が言及したように、その使用法は、参照される値が各スレッドで一意になるグローバル変数を定義することです。通常、その使用法には、現在の実行スレッドにリンクされているある種のコンテキスト情報を格納する必要があります。
これを Java EE 環境で使用して、Java EE に対応していない (HttpSession または EJB SessionContext にアクセスできない) クラスにユーザー ID を渡します。このようにして、セキュリティ ベースの操作に ID を使用するコードは、すべてのメソッド呼び出しで ID を明示的に渡す必要なく、どこからでも ID にアクセスできます。
ほとんどの Java EE 呼び出しの操作の要求/応答サイクルにより、ThreadLocal を設定および設定解除するための明確に定義されたエントリ ポイントと出口ポイントが提供されるため、このタイプの使用が容易になります。
ここでは特に目新しいことはありませんがThreadLocal
、Web アプリケーションで Bean Validation を使用するときに非常に役立つことを今日発見しました。検証メッセージはローカライズされていますが、デフォルトではLocale.getDefault()
. Validator
を別の で構成することはできますが、 をいつ呼び出すMessageInterpolator
かを指定する方法はありません。したがって、静的(または、さらに良いことに、必要な他のものを含む一般的なコンテナーを作成し、そこからカスタムを選択することができます。次のステップは、セッション値を使用する を作成するか、ロケールを選択して保存することです。あなたの参照でそれ。Locale
validate
ThreadLocal<Locale>
ThreadLocal
MessageInterpolator
Locale
ServletFilter
request.getLocale()
ThreadLocal
【参考】ThreadLocalは共有オブジェクトの更新問題を解決できない。同じスレッド内のすべての操作で共有される staticThreadLocal オブジェクトを使用することをお勧めします。[必須]remove() メソッドは、スレッドが頻繁に再利用されるスレッド プールを使用する場合は特に、ThreadLocal 変数によって実装する必要があります。そうしないと、後続のビジネス ロジックに影響し、メモリ リークなどの予期しない問題が発生する可能性があります。