どちらThreadLocal
のローカル変数Runnable
が優先されますか? パフォーマンス上の理由から。ローカル変数を使用すると、CPU キャッシュなどの機会が増えることを願っています。
4 に答える
ThreadLocal または Runnable のローカル変数のどちらが優先されますか。
スレッドのクラス (またはRunnable
) 内で宣言された変数がある場合、ローカル変数が機能し、. は必要ありませんThreadLocal
。
new Thread(new Runnable() {
// no need to make this a thread local because each thread already
// has their own copy of it
private SimpleDateFormat format = new SimpleDateFormat(...);
public void run() {
...
// this is allocated per thread so no thread-local
format.parse(...);
...
}
}).start();
一方、ThreadLocal
s は、一般的なコードを実行しているときに、スレッドごとに状態を保存するために使用されます。たとえば、SimpleDateFormat
は (残念ながら) スレッドセーフではないため、複数のスレッドによって実行されるコードで使用する場合はThreadLocal
、各スレッドが独自のバージョンの形式を取得できるように、 に格納する必要があります。
private final ThreadLocal<SimpleDateFormat> localFormat =
new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(...);
}
};
...
// if a number of threads run this common code
SimpleDateFormat format = localFormat.get();
// now we are using the per-thread format (but we should be using Joda Time :-)
format.parse(...);
これが必要な場合の例は、Web 要求ハンドラーです。スレッドは、制御できないある種のプールの Jetty ランド (たとえば) に割り当てられます。パスに一致する Web リクエストが届くと、Jetty がハンドラーを呼び出します。オブジェクトが必要ですがSimpleDateFormat
、その制限により、スレッドごとに 1 つ作成する必要があります。それはあなたが必要なときですThreadLocal
。複数のスレッドから呼び出される可能性のある再入可能コードを作成していて、スレッドごとに何かを保存したい場合。
代わりに、引数を自分に渡したい場合Runnable
は、独自のクラスを作成してから、コンストラクターにアクセスして引数を渡すことができます。
new Thread(new MyRunnable("some important string")).start();
...
private static class MyRunnable implements {
private final String someImportantString;
public MyRunnable(String someImportantString) {
this.someImportantString = someImportantString;
}
// run by the thread
public void run() {
// use the someImportantString string here
...
}
}
プログラムが 2 つ (ThreadLocal
またはローカル変数) のいずれかを正しく使用できる場合は常に、ローカル変数を選択してください。パフォーマンスが向上します。
ThreadLocal
メソッドの実行スコープを超えてスレッドごとの状態を保存するためのものです。明らかに、ローカル変数は、宣言のスコープを超えて保持することはできません。それらが必要な場合は、ThreadLocal
.
別のオプションはsynchronized
、共有メンバー変数へのアクセスを管理するために使用しています。これは複雑なトピックであり、他の場所で私よりも明確な人々によって説明および文書化されているため、ここではあえて説明しません。明らかに、これは「ローカル」ストレージの変形ではありません。スレッドセーフな方法で単一のリソースへのアクセスを共有することになります。
どちらもスレッド内で状態を維持するため、ローカル変数を使用できるのになぜ ThreadLocal が必要なのかについても混乱しました。しかし、多くの検索と実験の後、ThreadLocal が必要な理由がわかりました。
これまでに2つの用途が見つかりました-
- 同じ共有オブジェクト内にスレッド固有の値を保存する
- コードの N 層を介してパラメーターとして変数を渡す代替手段
1:
2 つのスレッドが同じオブジェクトを操作していて、両方のスレッドがこのオブジェクトを変更した場合、両方のスレッドが互いの変更を失い続けます。
このオブジェクトにスレッドごとに 2 つの個別の状態を持たせるには、このオブジェクトまたはその一部を ThreadLocal として宣言します。
もちろん、両方のスレッドが同じオブジェクトを共有しているため、ThreadLocal はここでのみ有益です。それらが異なるオブジェクトを使用している場合、オブジェクトが ThreadLocal である必要はありません。
2:
ThreadLocal の 2 番目の利点は、その実装方法の副作用のようです。
ThreadLocal 変数は、スレッドによって .set() にすることができ、その後、他の場所で .get() にすることができます。.get() は、このスレッドが他の場所で設定したのと同じ値を取得します。.get() と .set() を実行して実際にコードを書き留めるには、グローバルに利用可能なラッパーが必要です。
threadLocalVar.set() を実行すると、この現在のスレッドがキーであるグローバルな「マップ」内に配置されているかのようになります。
あたかも - someGlobalMap.put(Thread.currentThread(),threadLocalVar);
したがって、10 層下で、threadLocalVar.get() を実行すると、このスレッドが 10 層上に設定した値が取得されます。
threadLocalVar = someGlobalMap.get(Thread.currentThread());
したがって、10 番目のレベルの関数は、この変数をパラメーターとして持ち歩く必要はなく、正しいスレッドからのものかどうかを気にせずに .get() でアクセスできます。
最後に、ThreadLocal 変数は各スレッドへのコピーであるため、もちろん同期は必要ありません。以前、同期の代替として ThreadLocal を誤解していましたが、そうではありませんでした。この変数のアクティビティを今すぐ同期する必要がないのは、その副作用にすぎません。
これが役に立ったことを願っています。
この質問への答えは、変数は可能な限り小さい囲みスコープで宣言する必要があるという単純な規則です。AThreadLocal
は可能な最大のエンクロージング スコープであるため、多くのレキシカル スコープで必要とされるデータにのみ使用する必要があります。ローカル変数にできる場合は、そうする必要があります。