Java: проблема синхронизации с NumberFormat?

Я использую java.text.NumberFormat просто для преобразования чисел в более читаемые строки, с запятыми, разделяющими тысячи и т.д. В основном я определяю его как:

public static NumberFormat nf = NumberFormat.getInstance(Locale.US);

... и затем я просто вызываю nf.format(some_number) в любом потоке, где я хочу сделать читаемую версию числа. Но, глядя на JavaDoc, он говорит: "Числовые форматы обычно не синхронизированы. Рекомендуется создавать отдельные экземпляры формата для каждого потока. Если несколько потоков обращаются к формату одновременно, его необходимо синхронизировать извне".

Если я использую только метод format (number) объекта NumberFormat, может ли быть проблема синхронизации? Я попытался использовать NumberFormat.getInstance(Locale.US).format(number) вместо этого, но есть накладные расходы, связанные с этим, каждый раз, когда я чувствую, вероятно, не нужен. Действительно ли нужна внешняя синхронизация? Или есть более простой и эффективный способ выполнить одно и то же без NumFormat?

Спасибо!

Ответы

Ответ 1

Даже если формат - это единственный метод, который вы когда-либо вызывали, он по-прежнему не является потокобезопасным. Из-за этого у нас были проблемы с работой. Обычно мы либо создаем объекты NumberFormat на лету, либо используем ThreadLocal, как предположил Герко. Если вы хотите получить фантазию, вы можете подклассифицировать NumberFormat и в методе формата либо синхронизировать перед вызовом формата на делегате NumberFormat, либо использовать ThreadLocal для извлечения делегата.

Тем не менее, я считаю, что наиболее простой способ, особенно если вы собираетесь форматировать/разбирать несколько чисел подряд, - это использовать ThreadLocal вручную.

Ответ 2

Использовать ThreadLocal <NumberFormat> . Таким образом, каждый поток будет иметь свой собственный экземпляр NumberFormat и не будет необходимости синхронизировать и только минимальные издержки.

Ответ 3

Рассматривая исходный код NumberFormat и DecimalFormat, кажется, что для промежуточных результатов не используются поля, единственная проблема заключается в том, что сам формат (например, число дробных цифр) изменен через сеттеры, поэтому один поток может изменить его, а другой вызов format() обрабатывается, и это, конечно же, приведет к беспорядку.

Если вы никогда не используете сеттеры, тогда это должно быть хорошо, но, конечно, это только текущая реализация. Я бы не чувствовал себя комфортно в зависимости от того, что противоречит документам API. Использование ThreadLocal звучит как хороший компромисс.

Ответ 4

NumberFormat является abstract class. Его значение по умолчанию при вызове getInstance заключается в возврате экземпляра DecimalFormat. DecimalFormat использует кучу полей для сохранения своей позиции в процессе форматирования, шаблоны для префиксов и суффиксов, boolean, указывающие, следует ли использовать экспоненциальную нотацию и группировку тысяч, int для описания размера его целого и фракции частей и т.д.

Параметр ThreadLocal - отличный способ, если вы ожидаете какого-либо параллельного форматирования. Обратите внимание, что все подклассы класса abstract Format считаются небезопасными, поэтому даты форматирования также должны быть обработаны с этой осторожностью.

Ответ 5

Нет причин для совместного использования объекта NumberFormat. Да, у него могут быть проблемы с синхронизацией (посмотрите на источник для своей локали, и вы увидите, что они используют переменные-члены, даже для форматирования). Пока у вас проблемы с производительностью (что, скорее всего, не будет), просто создайте новый для каждого использования.

Изменить Как указывает Майкл Боргвардт, моя догадка о переменных-членах неверна. Но зачем волноваться? Используйте LocalThread, клонируйте NumberFormat или просто создайте новый. Эффективность с точки зрения создания объекта не является серьезной проблемой большую часть времени (но не всегда).