Делают ли переменные 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);
}
С этого момента formats
карта будет считываться различными классами, в разных потоках, каждый раз, чтобы вызвать метод format()
или parse()
одного или нескольких DateFormat
объектов, хранящихся на карте.
Имеет ли какой-либо из вышеперечисленных подходов смысл для описанного случая, или объявления ThreadLocal
должны быть статическими?