Синхронизация доступа к SimpleDateFormat

В javadoc для SimpleDateFormat указано, что SimpleDateFormat не синхронизирован.

«Форматы даты не синхронизированы. Рекомендуется создавать отдельные экземпляры формата для каждого streamа. Если несколько streamов обращаются к формату одновременно, его необходимо синхронизировать извне».

Но каков наилучший подход к использованию экземпляра SimpleDateFormat в многопоточной среде. Вот несколько вариантов, о которых я думал, я использовал варианты 1 и 2 в прошлом, но мне любопытно узнать, есть ли какие-то лучшие альтернативы или какие из этих вариантов будут предлагать лучшую производительность и параллелизм.

Вариант 1: при необходимости создавайте локальные экземпляры

public String formatDate(Date d) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); return sdf.format(d); } 

Вариант 2. Создайте экземпляр SimpleDateFormat как переменной classа, но синхронизируйте его с ним.

 private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); public String formatDate(Date d) { synchronized(sdf) { return sdf.format(d); } } 

Вариант 3. Создайте ThreadLocal для хранения другого экземпляра SimpleDateFormat для каждого streamа.

 private ThreadLocal tl = new ThreadLocal(); public String formatDate(Date d) { SimpleDateFormat sdf = tl.get(); if(sdf == null) { sdf = new SimpleDateFormat("yyyy-MM-hh"); tl.set(sdf); } return sdf.format(d); } 

  1. Создание SimpleDateFormat дорого . Не используйте это, если это сделано редко.

  2. Хорошо, если вы можете жить с некоторой блокировкой. Используйте, если formatDate () не используется много.

  3. Самый быстрый вариант, если вы повторно используете streamи ( пул streamов ). Использует больше памяти, чем 2. и имеет более высокую начальную нагрузку.

Для приложений оба варианта 2. и 3. являются жизнеспособными. Что лучше для вашего дела, зависит от вашего варианта использования. Остерегайтесь преждевременной оптимизации. Только делайте это, если считаете, что это проблема.

Для библиотек, которые будут использоваться сторонней стороной, я бы использовал вариант 3.

Другой вариант – Commons Lang FastDateFormat, но вы можете использовать его только для форматирования даты, а не для parsingа.

В отличие от Joda, он может функционировать как замена для форматирования. (Обновление: Начиная с версии 3.3.2, FastDateFormat может создавать FastDateParser , который является заменой streamовой замены для SimpleDateFormat)

Если вы используете Java 8, вы можете использовать java.time.format.DateTimeFormatter :

Этот class является неизменным и streamобезопасным.

например:

 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); String str = new java.util.Date().toInstant() .atZone(ZoneId.systemDefault()) .format(formatter); 

В Commons Lang 3.x теперь есть FastDateParser, а также FastDateFormat. Он streamобезопасен и быстрее, чем SimpleDateFormat. Он также использует те же спецификации формата / описания parsingа, что и SimpleDateFormat.

Не используйте SimpleDateFormat, вместо этого используйте DateTimeFormatter joda-time. Это немного сложнее в синтаксическом анализе, и это не совсем то, что требуется для замены SimpleDateFormat, но joda-time гораздо более дружелюбен с точки зрения безопасности и производительности.

Я бы сказал, создайте простой class-оболочку для SimpleDateFormat, который синхронизирует доступ к parse () и format () и может использоваться как замена для замены. Более надежный, чем ваш вариант №2, менее громоздкий, чем ваш вариант №3.

Похоже, что создание SimpleDateFormat несинхронизировано – это плохое дизайнерское решение со стороны разработчиков Java API; Я сомневаюсь, что кто-то ожидает, что формат () и parse () должны быть синхронизированы.

Другим вариантом является сохранение экземпляров в streamобезопасной очереди:

 import java.util.concurrent.ArrayBlockingQueue; private static final int DATE_FORMAT_QUEUE_LEN = 4; private static final String DATE_PATTERN = "yyyy-MM-dd HH:mm:ss"; private ArrayBlockingQueue dateFormatQueue = new ArrayBlockingQueue(DATE_FORMAT_QUEUE_LEN); // thread-safe date time formatting public String format(Date date) { SimpleDateFormat fmt = dateFormatQueue.poll(); if (fmt == null) { fmt = new SimpleDateFormat(DATE_PATTERN); } String text = fmt.format(date); dateFormatQueue.offer(fmt); return text; } public Date parse(String text) throws ParseException { SimpleDateFormat fmt = dateFormatQueue.poll(); if (fmt == null) { fmt = new SimpleDateFormat(DATE_PATTERN); } Date date = null; try { date = fmt.parse(text); } finally { dateFormatQueue.offer(fmt); } return date; } 

Размер dateFormatQueue должен быть чем-то близким к расчетному числу streamов, которые могут одновременно вызвать эту функцию одновременно. В худшем случае, когда большее количество streamов, чем это число, фактически использует все экземпляры одновременно, будут созданы экземпляры SimpleDateFormat, которые не могут быть возвращены dateFormatQueue, потому что они заполнены. Это не приведет к ошибке, оно просто повлечет за собой штраф за создание некоторого SimpleDateFormat, который используется только один раз.

Я только что реализовал это с помощью варианта 3, но сделал несколько изменений кода:

  • ThreadLocal обычно должен быть статическим
  • Кажется, что более чистое переопределение initialValue () вместо проверки if (get () == null)
  • Вы можете установить языковой стандарт и часовой пояс, если вы действительно не хотите, чтобы настройки по умолчанию (по умолчанию очень подвержены ошибкам Java)

     private static final ThreadLocal tl = new ThreadLocal() { @Override protected SimpleDateFormat initialValue() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-hh", Locale.US); sdf.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); return sdf; } }; public String formatDate(Date d) { return tl.get().format(d); } 

Представьте, что ваше приложение имеет один stream. Зачем вам синхронизировать доступ к переменной SimpleDataFormat?

  • Как вы форматируете день месяца, чтобы сказать «11-й», «21-й» или «23-й» (порядковый индикатор)?
  • SimpleDateFormat parse теряет часовой пояс
  • Java SimpleDateFormat всегда возвращает январь за месяц
  • Сравнение времени неверно при выборе 12:00
  • Как форматировать строку даты в java?
  • java.text.ParseException: Непревзойденная дата
  • Что это за формат даты? 2011-08-12T20: 17: 46.384Z
  • Как разбирать дату?
  • Строка формата SimpleDateFormat и локали
  • Каковы форматы дат в classе SimpleDateFormat?
  • Y возвращает 2012, а y возвращает 2011 в SimpleDateFormat
  • Interesting Posts

    Почему нулевая ссылка печатается как “null”

    Bootstrap 3 Свернуть

    Как использовать Zxing в android

    ListFragment OnListItemClick не вызывается

    ASP.NET MVC – сеанс имеет значение null

    Алгоритм «приятных» интервалов сетки на графике

    Как получить список всего программного обеспечения, которое запускается автоматически при запуске Windows 7?

    Swift – Захват ключа от NSViewController

    ASP.net Получение ошибки «Доступ к пути запрещен» при попытке загрузить файлы на мой веб-сервер Windows Server 2008 R2

    Разница между IEnumerable Count () и длиной

    разница между prop () и attr () в jQuery и когда использовать attr () и prop ()

    Восстановление EFI Windows 8 после установки Ubuntu EFI

    sqrt из math.h вызывает ошибку компоновщика «неопределенная ссылка на sqrt» только тогда, когда аргумент не является константой

    отправка нескольких форм с помощью AJAX

    Какова разница между типичными параметрами функции и типизированными параметрами протокола?

    Давайте будем гением компьютера.