ThreadLocal и SimpleDateFormat массив
Используя шаблон, очень похожий на тот, который описан в недавнем вопросе, для многопоточного приложения, я получаю странные значения даты (например, годы, например, 2025 или 2035, когда в исходных данных явно нет такого значения). Кажется, что возникает проблема параллелизма.
Исходный код выглядит примерно так
// Various Java DateFormat patterns, e.g. "yyyy-MM-dd".
private static final String[] DATE_PATTERNS = new String[] {...};
private static SimpleDateFormat[] getFormats(final String[] patterns)
{
ThreadLocal<SimpleDateFormat[]> LOCAL_FORMATS = new ThreadLocal<SimpleDateFormat[]>()
{
@Override
protected SimpleDateFormat[] initialValue()
{
List<SimpleDateFormat> formatList = new ArrayList<SimpleDateFormat>();
for (String pattern:patterns)
{
formatList.add(new SimpleDateFormat(pattern));
}
return formatList.toArray(new SimpleDateFormat[formatList.size()]);
}
};
return LOCAL_FORMATS.get(); // Create a thread-local copy
}
private static final SimpleDateFormat[] DATE_FORMATS = getFormats(DATE_PATTERNS);
После статической инициализации DATE_FORMATS
к массиву обращаются многочисленные классы, которые в свою очередь используют SimpleDateFormat
объекты массива для разбора или форматирования нескольких строк даты.
Может ли быть какая-либо проблема параллелизма в таком сценарии использования, особенно с учетом использования ThreadLocal
?
2 ответа
Да, могут быть проблемы с параллелизмом. Ваша локальная переменная потока не предназначена для каких-либо целей. Он используется только при инициализации класса для временного хранения массива форматов даты, который немедленно извлекается и сохраняется в статической константе.
После этого все потоки всегда используют одни и те же экземпляры форматов даты одновременно, не получая их из какой-либо локальной переменной потока.
Код должен быть:
private static final String[] DATE_PATTERNS = new String[] {...};
private static final ThreadLocal<SimpleDateFormat[]> DATE_FORMATS =
new ThreadLocal<SimpleDateFormat[]>() {
@Override
protected SimpleDateFormat[] initialValue() {
List<SimpleDateFormat> formatList = new ArrayList<SimpleDateFormat>();
for (String pattern : DATE_PATTERNS)
{
formatList.add(new SimpleDateFormat(pattern));
}
return formatList.toArray(new SimpleDateFormat[formatList.size()]);
}
};
public static SimpleDateFormat[] getDateFormats() {
return DATE_FORMATS.get();
}
Я бы также использовал неизменяемый List<SimpleDateFormat>
а не массив, чтобы быть безопаснее.
// Various Java DateFormat patterns, e.g. "yyyy-mm-dd".
Формат "гггг-мм-дд", скорее всего, даст вам странные результаты, потому что "мм" - это минуты, а не месяцы. Из Javadoc:
M Month in year Month July; Jul; 07
...
m Minute in hour Number 30