Java 変数がインスタンス変数として使用された場合 (たとえば、スレッド ローカル オブジェクトを生成するメソッドで)、 JavaThreadLocal
変数はスレッド ローカル値を生成しますか?それとも、そのためには常に静的でなければなりませんか?
例として、スレッドセーフではないクラスの初期化にコストがかかるいくつかのオブジェクトを、単一の静的初期化ブロックでインスタンス化し、単一のクラスの静的変数 (たとえば、Map
データそれ以降、多数の異なるスレッドによる集中的な処理に使用されます。
スレッド セーフを実現するには、明らかに各静的オブジェクトの異なるコピーを渡す必要があります。たとえば、DateFormat
異なるスレッド間で安全に使用する必要がある Java オブジェクトです。
Web で見つけることができる多くの例では、アプローチは各ThreadLocal
変数を個別に宣言し、メソッドで新しいオブジェクトをインスタンス化し、メソッドをinitialValue()
使用get()
してスレッドローカル インスタンスを取得しているようです。
この方法は、作成するオブジェクトが数十または数百あり、それぞれに独自の初期化パラメータがある場合、あまり効率的ではありません。たとえば、SimpleDateFormat
それぞれ異なる日付パターンを持つ多くのオブジェクト。
オブジェクトのインスタンス化が、反復ごとに異なる値を生成するループで実行できる場合、対応するオブジェクトを適切に初期化することによって各値が作成された後に、スレッドローカル インスタンスを生成するための汎用メソッドが必要になります。
上記に基づいて、initialValue() へのすべての呼び出しで同じ参照が生成されるため、次の一般的な静的メソッドは機能しません。
// Each value is an object initialized prior to calling getLocal(...)
public static final <T> T getLocal(final T value)
{
ThreadLocal<T> local = new ThreadLocal<T>()
{
@Override
protected T initialValue()
{
return value;
}
};
return local.get();
}
代わりに、initialValue() 内で新しいオブジェクトを作成するメカニズムが必要です。したがって、唯一の一般的なアプローチは、おそらく次のようなパターンでリフレクションを使用することです
private static final <T> T getLocal(
final Constructor<T> constructor, final Object[] initargs)
{
ThreadLocal<T> local = new ThreadLocal<T>()
{
@Override
protected T initialValue()
{
T value = null;
try // Null if the value object cannot be created
{
value = constructor.newInstance(initargs);
}
catch (Exception e)
{
}
return value;
}
};
return local.get();
}
次に、もちろん、型固有のオプションがありThreadLocal
、ループ内でパターンを使用して各変数を宣言することができます。
たとえば、 の場合DateFormat
、単一の静的初期化ブロックで次のことができます。
private static String[] patterns = ... // Get date patterns
private static DateFormat format;
public static Map<String, DateFormat> formats = new HashMap<String, DateFormat>();
static
{
for (final String pattern:patterns)
{
format = new ThreadLocal<DateFormat>()
{
@Override
protected DateFormat initialValue()
{
return new SimpleDateFormat(pattern);
}
}.get();
formats.put(pattern, format);
}
それ以降、マップは、マップに格納されている1 つ以上のオブジェクトのorメソッドformats
を呼び出すために、毎回異なるスレッドを介して異なるクラスによって読み取られます。format()
parse()
DateFormat
上記のアプローチのいずれかが説明されている場合に意味がありますか、またはThreadLocal
宣言を静的にする必要がありますか?