Вопрос-ответ

Why is Java's SimpleDateFormat not thread-safe? [duplicate]

Почему SimpleDateFormat в Java не потокобезопасен?

Пожалуйста, расскажите на примере кода, почему SimpleDateFormat не потокобезопасен. В чем проблема в этом классе? Проблема с функцией форматирования SimpleDateFormat? Пожалуйста, приведите код, который демонстрирует эту ошибку в классе.

FastDateFormat потокобезопасен. Почему? в чем разница между SimpleDateFormat и FastDateFormat?

Пожалуйста, объясните с помощью кода, который демонстрирует эту проблему?

Переведено автоматически
Ответ 1

SimpleDateFormat сохраняет промежуточные результаты в полях экземпляра. Таким образом, если один экземпляр используется двумя потоками, они могут искажать результаты друг друга.

Просмотр исходного кода показывает, что существует Calendar поле экземпляра, которое используется при операциях с DateFormat / SimpleDateFormat .

Например, parse(..) вызывает calendar.clear() сначала, а затем calendar.add(..). Если другой поток вызовет parse(..) до завершения первого вызова, он очистит календарь, но другой вызов будет ожидать, что он будет заполнен промежуточными результатами вычисления.

Один из способов повторного использования форматов даты без нарушения потокобезопасности - поместить их в ThreadLocal - некоторые библиотеки так и делают. Это если вам нужно использовать один и тот же формат несколько раз в одном потоке. Но если вы используете контейнер сервлета (с пулом потоков), не забудьте очистить thread-local после завершения работы.

Честно говоря, я не понимаю, зачем им нужно поле экземпляра, но так оно и есть. Вы также можете использовать joda-time DateTimeFormat, который потокобезопасен.

Ответ 2

SimpleDateFormat это конкретный класс для форматирования и синтаксического анализа дат с учетом локали.

Из JavaDoc,


Но форматы даты не синхронизированы. Рекомендуется создавать отдельные экземпляры формата для каждого потока. Если несколько потоков обращаются к формату одновременно, it must be synchronized externally.


Чтобы сделать класс SimpleDateFormat потокобезопасным, рассмотрим следующие подходы :


  • Создавайте новый экземпляр SimpleDateFormat каждый раз, когда вам нужно его использовать. Хотя это потокобезопасный подход, это самый медленный из возможных.

  • Используйте синхронизацию. Это плохая идея, потому что вы никогда не должны ограничивать свои потоки на сервере.

  • Используйте ThreadLocal . Это самый быстрый подход из 3 (см. http://www.javacodegeeks.com/2010/07/java-best-practices-dateformat-in.html).

Ответ 3

DateTimeFormatter в Java 8 это неизменяемая и потокобезопасная альтернатива SimpleDateFormat.

Ответ 4

ThreadLocal + SimpleDateFormat = SimpleDateFormatThreadSafe

package com.foocoders.text;

import java.text.AttributedCharacterIterator;
import java.text.DateFormatSymbols;
import java.text.FieldPosition;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

public class SimpleDateFormatThreadSafe extends SimpleDateFormat {

private static final long serialVersionUID = 5448371898056188202L;
ThreadLocal<SimpleDateFormat> localSimpleDateFormat;

public SimpleDateFormatThreadSafe() {
super();
localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat();
}
};
}

public SimpleDateFormatThreadSafe(final String pattern) {
super(pattern);
localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(pattern);
}
};
}

public SimpleDateFormatThreadSafe(final String pattern, final DateFormatSymbols formatSymbols) {
super(pattern, formatSymbols);
localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(pattern, formatSymbols);
}
};
}

public SimpleDateFormatThreadSafe(final String pattern, final Locale locale) {
super(pattern, locale);
localSimpleDateFormat = new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(pattern, locale);
}
};
}

public Object parseObject(String source) throws ParseException {
return localSimpleDateFormat.get().parseObject(source);
}

public String toString() {
return localSimpleDateFormat.get().toString();
}

public Date parse(String source) throws ParseException {
return localSimpleDateFormat.get().parse(source);
}

public Object parseObject(String source, ParsePosition pos) {
return localSimpleDateFormat.get().parseObject(source, pos);
}

public void setCalendar(Calendar newCalendar) {
localSimpleDateFormat.get().setCalendar(newCalendar);
}

public Calendar getCalendar() {
return localSimpleDateFormat.get().getCalendar();
}

public void setNumberFormat(NumberFormat newNumberFormat) {
localSimpleDateFormat.get().setNumberFormat(newNumberFormat);
}

public NumberFormat getNumberFormat() {
return localSimpleDateFormat.get().getNumberFormat();
}

public void setTimeZone(TimeZone zone) {
localSimpleDateFormat.get().setTimeZone(zone);
}

public TimeZone getTimeZone() {
return localSimpleDateFormat.get().getTimeZone();
}

public void setLenient(boolean lenient) {
localSimpleDateFormat.get().setLenient(lenient);
}

public boolean isLenient() {
return localSimpleDateFormat.get().isLenient();
}

public void set2DigitYearStart(Date startDate) {
localSimpleDateFormat.get().set2DigitYearStart(startDate);
}

public Date get2DigitYearStart() {
return localSimpleDateFormat.get().get2DigitYearStart();
}

public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) {
return localSimpleDateFormat.get().format(date, toAppendTo, pos);
}

public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
return localSimpleDateFormat.get().formatToCharacterIterator(obj);
}

public Date parse(String text, ParsePosition pos) {
return localSimpleDateFormat.get().parse(text, pos);
}

public String toPattern() {
return localSimpleDateFormat.get().toPattern();
}

public String toLocalizedPattern() {
return localSimpleDateFormat.get().toLocalizedPattern();
}

public void applyPattern(String pattern) {
localSimpleDateFormat.get().applyPattern(pattern);
}

public void applyLocalizedPattern(String pattern) {
localSimpleDateFormat.get().applyLocalizedPattern(pattern);
}

public DateFormatSymbols getDateFormatSymbols() {
return localSimpleDateFormat.get().getDateFormatSymbols();
}

public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols) {
localSimpleDateFormat.get().setDateFormatSymbols(newFormatSymbols);
}

public Object clone() {
return localSimpleDateFormat.get().clone();
}

public int hashCode() {
return localSimpleDateFormat.get().hashCode();
}

public boolean equals(Object obj) {
return localSimpleDateFormat.get().equals(obj);
}

}

https://gist.github.com/pablomoretti/9748230

java