Использование ThreadLocal в переменных экземпляра

Переменные Java ThreadLocalпроизводят локальные значения потока, если они используются в качестве переменных экземпляра(например, в методе, который генерирует локальные объекты потока), или они должны всегда быть статичным, чтобы сделать это?

В качестве примера предположим типичный сценарий, в котором несколько дорогостоящих для инициализации объектов класса, не являющегося потокобезопасным, должны быть созданы в одном статическом блоке инициализации, сохраненном в статических переменных одного класса (например, в структуре данных Map) и с тех пор используется для интенсивной обработки множеством различных потоков.

Очевидно, что для обеспечения безопасности потоков необходимо передавать разные копии каждого статического объекта. Например, объекты Java DateFormat, которые необходимо безопасно использовать в разных потоках.

Во многих примерах, которые можно найти в Интернете, кажется, что подход заключается в отдельном объявлении каждой переменной 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);
}

С этого момента карта форматовбудет считываться разными классы в разных потоках каждый раз для вызова метода format()или parse()одного или нескольких объектов DateFormat, хранящихся на карте.

Имеет ли смысл любой из вышеперечисленных подходов для описанного случая, или объявления ThreadLocalдолжны быть статическими?

5
задан PNS 11 March 2012 в 14:20
поделиться