33

I have to print many formatted decimal values in many threads in parallel. To format the decimal values I use a java.text.DecimalFormat configured by a pattern. I am aware of the warning from the java doc of DecimalFormat:

Decimal formats are generally not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.

But I don’t know if this warning applies to my scenario: I configure the java.text.DecimalFormat once when the application starts (and store the Formatter in a final field). After that I ONLY use the format(double) method.

The reason why I want to do this is: I don’t want to lose performance by creating a new DecimalFormat instance every time I need to print a formatted number.

I looked at the DecimalFormat.format(double) code and it looks to be thread safe, but I am not sure.

Could you please confirm that the usage of DecimalFormat.format(double) is eventually thread safe, when not changing the configuration of the formatter, or explain why it is not?

pants
  • 192
  • 13
Ralph
  • 118,862
  • 56
  • 287
  • 383
  • 2
    A "workarround" is to use ThreadLocal, but is is not the question. – Ralph Dec 08 '10 at 12:02
  • Another workaround is to synchronize on the DecimalFormat object on conversions. Either (a) you do few conversions and synchronization will not affect performance much, or (b) you do many conversions, in which case DecimalFormat objects can probably be reused for conversions in the same thread, therefore their construction costs should be negligible. – Gintautas Miliauskas Dec 08 '10 at 12:38

3 Answers3

33

Just use this thread-safe snippet for NumberFormat:

static ThreadLocal<NumberFormat> numberFormat = new ThreadLocal<NumberFormat>() {
    @Override
    public NumberFormat initialValue() {
        return new DecimalFormat("00000");
    }
};

Or in Java 8, as Jesper said in comment:

private static ThreadLocal<NumberFormat> numberFormatter = 
                  ThreadLocal.withInitial(() -> new DecimalFormat("00000"));
Dariusz
  • 21,561
  • 9
  • 74
  • 114
  • 8
    Which can be converted to a lambda expression in Java 8: `private static ThreadLocal numberFormatter = ThreadLocal.withInitial(() -> new DecimalFormat("0.00"));` – Jesper N Dec 14 '16 at 12:58
  • 5
    NEGATIVE. ThreadLocal instances have a lot of unpleasant side effects (for example memory leaks in JEE environments). It is a really bad idea to use it for such a negligible optimization. – 30thh Nov 04 '19 at 16:45
  • @30thh it's good that you brought it up, it's a good point and something to watch out for. Remember `ThreadLocal` is a tool and as all tools should be used with care. As far as memory leaks go, I've seen much more bad uses of Map on beans than I've seen of ThreadLocal. I'd like to hear more unpleasant side effects, as there are supposedly `a lot` of them. As for optimizations - it all depends on the architecture of the OP application. If he had some kind of logic looped above the place with `new NumberFormat` then he could be saving a lot. We do not know. – Dariusz Nov 05 '19 at 06:19
  • @Dariusz Please can you write more about bad usage od Map? I'm asking for myself, it's not related to the OP. – horvoje May 28 '21 at 16:05
  • I meant map fields in long-lived beans, when used badly they can cause memory leaks and concurrent modification exceptions and memory synchronization issues – Dariusz May 28 '21 at 16:08
24

While the current implementation may be eventually thread-safe, there is no such guarantee for coming implementations, or for other JREs.

Have you verified that avoiding new DecimalFormat() is a measurable performance gain in your application?

Dariusz
  • 21,561
  • 9
  • 74
  • 114
mfx
  • 7,168
  • 26
  • 29
  • On One test I happen to find out that it takes about to ~23ms to instantiate NumberFormat and Decimal format instance, which I think is a wasted execution time, any workaround advised? – Somasundaram Sekar Feb 03 '16 at 07:48
  • 2
    DecimalFormat is not thread safe. See: https://docs.oracle.com/javase/8/docs/api/java/text/DecimalFormat.html "Decimal formats are generally not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally." – Vitalii Pro Jul 31 '18 at 14:31
7

Current Hotspot implementation for DecimalFormat make the call to DecimalFormat.format(double) thread-safe if you do not call other methods on this instance. However it is strongly advised not to rely on this (maybe) temporary behaviour.

Have you considered using a ThreadLocal variable to avoid too many new DecimalFormat()?

Guillaume
  • 5,535
  • 1
  • 24
  • 30
  • Per http://jonamiller.com/2015/12/21/decimalformat-is-not-thread-safe/ `DecimalFormat` is no longer thread-safe in HotSpot as of Java 8. – Gili Jan 12 '19 at 23:33