Синхронизация на SimpleDateFormat против клона - PullRequest
15 голосов
/ 18 февраля 2011

Мы знаем, что классы dateformat не являются поточно-ориентированными. У меня есть многопоточный сценарий, в котором необходимо использовать форматы даты. Я не могу действительно создать новый экземпляр в новом потоке, так как SimpledateFormat создание кажется дорогим (конструктор заканчивает тем, что вызывает "compile", который дорог). После некоторых тестов у меня остались только два варианта:

  1. Внешняя синхронизация - я действительно не хочу этого делать
  2. Клонирование в каждой теме - Не знаете, есть ли какие-то зацепки?

Есть предложения?

Если ребята сталкивались с этим раньше, в каком направлении вы пошли.

Примечание : аналогичный вопрос задавался ранее, но он был закрыт, указывая на пакет apache. Я не могу использовать новые библиотеки для этого. И я также читал этот похожий вопрос на SO

Ответы [ 7 ]

13 голосов
/ 18 февраля 2011

Что если вы создали класс, который будет форматировать даты, используя пул фиксированного размера предварительно созданных объектов SimpleDateFormat в режиме циклического перебора? Учитывая, что неоспоримая синхронизация обходится дешево, она может синхронизироваться с объектом SimpleDateFormat, амортизируя коллизии по всему набору.

Таким образом, может быть 50 форматеров, каждый из которых используется по очереди - коллизия, и, следовательно, конфликт блокировки может произойти, только если 51 дата была фактически отформатирована одновременно.

РЕДАКТИРОВАТЬ 2011-02-19 (PST)

Я реализовал фиксированный пул, как предложено выше, код для которого (включая тест), доступен на моем веб-сайте .

Ниже приведены результаты для четырехъядерного процессора AMD Phenom II 965 BE, работающего на клиентской виртуальной машине Java 6 SE:

2011-02-19 15:28:13.039 : Threads=10, Iterations=1,000,000
2011-02-19 15:28:13.039 : Test 1:
2011-02-19 15:28:25.450 :   Sync      : 12,411 ms
2011-02-19 15:28:37.380 :   Create    : 10,862 ms
2011-02-19 15:28:42.673 :   Clone     : 4,221 ms
2011-02-19 15:28:47.842 :   Pool      : 4,097 ms
2011-02-19 15:28:48.915 : Test 2:
2011-02-19 15:29:00.099 :   Sync      : 11,184 ms
2011-02-19 15:29:11.685 :   Create    : 10,536 ms
2011-02-19 15:29:16.930 :   Clone     : 4,184 ms
2011-02-19 15:29:21.970 :   Pool      : 3,969 ms
2011-02-19 15:29:23.038 : Test 3:
2011-02-19 15:29:33.915 :   Sync      : 10,877 ms
2011-02-19 15:29:45.180 :   Create    : 10,195 ms
2011-02-19 15:29:50.320 :   Clone     : 4,067 ms
2011-02-19 15:29:55.403 :   Pool      : 4,013 ms

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

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

Прежде чем принять окончательное решение, так или иначе, я бы хотел провести тщательное тестирование на различных JVM, версиях и различных объектах такого типа. Старые JVM и те, что на небольших устройствах, таких как карманные компьютеры и телефоны, могут иметь гораздо больше накладных расходов на создание объектов и сборку мусора. И наоборот, они могут иметь больше накладных расходов при неоспоримой синхронизации.

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

РЕДАКТИРОВАТЬ 2011-02-19 (PST)

Также интересны несопровождаемые однопоточные результаты. В этом случае пул работает наравне с одним синхронизированным объектом. Это будет означать, что пул является наилучшей альтернативой в целом, поскольку он обеспечивает отличную производительность, когда он удовлетворен, а когда нет. Немного удивительно, что клонирование менее эффективно при однопоточности.

2011-02-20 13:26:58.169 : Threads=1, Iterations=10,000,000
2011-02-20 13:26:58.169 : Test 1:
2011-02-20 13:27:07.193 :   Sync      : 9,024 ms
2011-02-20 13:27:40.320 :   Create    : 32,060 ms
2011-02-20 13:27:53.777 :   Clone     : 12,388 ms
2011-02-20 13:28:02.286 :   Pool      : 7,440 ms
2011-02-20 13:28:03.354 : Test 2:
2011-02-20 13:28:10.777 :   Sync      : 7,423 ms
2011-02-20 13:28:43.774 :   Create    : 31,931 ms
2011-02-20 13:28:57.244 :   Clone     : 12,400 ms
2011-02-20 13:29:05.734 :   Pool      : 7,417 ms
2011-02-20 13:29:06.802 : Test 3:
2011-02-20 13:29:14.233 :   Sync      : 7,431 ms
2011-02-20 13:29:47.117 :   Create    : 31,816 ms
2011-02-20 13:30:00.567 :   Clone     : 12,382 ms
2011-02-20 13:30:09.079 :   Pool      : 7,444 ms
3 голосов
/ 18 февраля 2011

Поскольку ThreadLocal в вашем случае невозможен, вам следует использовать пул.Получите или создайте новый экземпляр, используйте его и потом поместите в пул.

2 голосов
/ 19 мая 2012

Необходимо указать, что вариант 2: " Клонирование в каждом потоке - Не знаете, есть ли какие-то уловки? " является только жизнеспособным вариантом, потому что SimpleDateFormat и DateFormat реализуют клон deep (). Клон shallow не будет более поточно-ориентированным, чем просто использование одного и того же экземпляра.

Причина, по которой DateFormat и любые его подклассы не являются поточно-ориентированными, вызвана тем, что DateFormat использует внутренний экземпляр Calendar, хранящийся в качестве переменной-члена. Вызовы format () или parse () полагаются на поля clear (), set () и get () экземпляра Calendar. Любые одновременные вызовы могут повредить внутреннее состояние календаря.

Это также тот факт, что классы реализуют deep clone (), что делает его относительно медленным.

2 голосов
/ 18 февраля 2011

Мы перешли на использование потоковобезопасного FastDateFormat

2 голосов
/ 18 февраля 2011

Как подсказывает похожий вопрос, который вы связали, он действительно зависит от вашего варианта использования.Если вы повторно используете свои потоки, то лучшим выбором будет использование значения ThreadLocal.Если вы не используете свои потоки повторно, вы должны сравнить синхронизацию и создание объекта.Если ваши синхронизированные задачи выполняются долго (в случае, если вам нужно проанализировать много дат одновременно), ваши потоки могут в конечном итоге ждать дольше, чем потребуется для создания объекта.

Лично я обычно шел по пути использования значения ThreadLocal, но это потому, что все мои варианты использования были с повторным использованием потока.

2 голосов
/ 18 февраля 2011

Я использовал ThreadLocal для хранения экземпляра каждого формата даты, который я использую.

0 голосов
/ 18 февраля 2011

У меня есть небольшой кеш SimpleDateFormats, который вы можете заблокировать.Каждая блокировка будет стоить вам 1-2 микросекунды, но вы можете захотеть узнать, сколько времени каждый раз стоит создавать новый объект.

...