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
Другие вопросы по тегам