5

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宣言を静的にする必要がありますか?

4

3 に答える 3

8

見出しの質問に答えるために、ThreadLocal各スレッドにそのThreadLocalインスタンスの個別の値を提供します。したがって、2 つのインスタンスが異なる場所にある場合、各スレッドはそれぞれに個別の値を持ちます。ThreadLocalこれが、 s がしばしば静的である理由です。スレッドごとに変数の個別の値が必要なThreadLocal場合は、JVM でその変数に必要な値は 1 つだけです。

AH の回答は非常に優れており、さらにバリエーションを提案します。マップの定義ではなく、呼び出しコードに日付形式を制御したい場合があるようです。次のようなコードでそれを行うことができます。

public class DateFormatSupplier {
    private static final Map<String, ThreadLocal<DateFormat>> localFormatsByPattern = new HashMap<String, ThreadLocal<DateFormat>>();

    public static DateFormat getFormat(final String pattern) {
        ThreadLocal<DateFormat> localFormat;
        synchronized (localFormatsByPattern) {
            localFormat = localFormatsByPattern.get(pattern);
            if (localFormat == null) {
                localFormat = new ThreadLocal<DateFormat>() {
                    @Override
                    protected DateFormat initialValue() {
                        return new SimpleDateFormat(pattern);
                    }
                };
                localFormatsByPattern.put(pattern, localFormat);
            }
        }
        return localFormat.get();
    }
}

ThreadLocals を遅延して作成する場所。

于 2012-03-11T12:49:31.417 に答える
7

Java ThreadLocal変数は、インスタンス変数として使用される場合、スレッドローカル値を生成しますか?

はい、彼らはやる。考えてみてください。ThreadLocalは静的または非静的ではなく、への参照のみが静的ThreadLocalであるかどうかです。オブジェクト自体は常に同じように見えます。

上記のアプローチのいずれかが説明されているケースに意味がありますか、それともThreadLocal宣言は静的である必要がありますか?

あまり。

例:

[DateFormat] format = new ThreadLocal<DateFormat>()
    {...}.get();
formats.put(pattern, format);

つまり、常に新しいを作成し、すぐThreadLocalに呼び出して、結果(ではなく)をマップに配置します。これは、フォーマット自体も再利用しないことを意味します。getThreadLocalThreadLocal

したがって、私があなたのユースケースを理解している限り、あなたはこのようなものが欲しいかもしれません:

public class XXX {
    private final static Map<String, SimpleDateFormatThreadLocal> formatMap = 
        new HashMap<String, SimpleDateFormatThreadLocal>();

    static {
        String[] patterns = {"a", "b", "c"};
        for(String pattern: patterns){
            formatMap.put(pattern, new SimpleDateFormatThreadLocal(pattern));
        }
    }

    private static class SimpleDateFormatThreadLocal extends ThreadLocal<SimpleDateFormat> {
        private final String pattern;

        public SimpleDateFormatThreadLocal(String pattern) {
            this.pattern = pattern;
        }
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat(pattern);
        }
    }
}

使用例は次のようになります。

public void run(){
    String s = formatMap.get("a").get().format(new Date());
    System.out.println(s);
}

ここにあなた

  • ThreadLocalオブジェクトを再利用する
  • オブジェクトを再利用しDateFormatます(もちろんスレッドごとに)
  • DateFormat一部のスレッドで使用されないを作成することは避けてください。
于 2012-03-11T12:48:04.260 に答える