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 или просто создайте новый. Эффективность с точки зрения создания объекта не является серьезной проблемой большую часть времени (но не всегда).