Recently I was asked by one of our team mate that during load testing, order creation was failing because of wrong inputs(One of the application I am working on is an Order Management Application) . However, the unit tests, with the same input was working fine. Investigation into it, led to (once again) an old jdk “designed” bug 4146524 (or feature). After seeing such issues in a number of applications, I am sure a number of developers are yet not aware of Format objects are not thread-safe. So if you create a Format
object (or a MessageFormat
, NumberFormat
, DecimalFormat
, ChoiceFormat
, DateFormat
or SimpleDateFormat
object), it cannot be shared among threads.
A number of write ups are available on internet to make sure your Format objects can work on multi threaded environment. Recreating it below, if it can help some.
Thread unsafe implementation
package aminur.test.formatters; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.ParseException; public final class ThreadUnsafePriceFormatter { // the price formats private static final DecimalFormat pointSeparatedFormat; static { DecimalFormatSymbols symbols = new DecimalFormatSymbols(); symbols.setDecimalSeparator('.'); pointSeparatedFormat = new DecimalFormat("#0.00", symbols); } private ThreadUnsafePriceFormatter() { } public static String formatPointSeparated(double price) { return pointSeparatedFormat.format(price); } public static Number parsePointSeparated(String price) throws ParseException { return pointSeparatedFormat.parse(price); } }Problem, In multi-threaded environment, it can throw exception.
private static void testThreadUnSafeFormatter() throws Exception { Callable<Number> task = new Callable<Number>() { public Number call() throws Exception { return ThreadUnsafePriceFormatter.parsePointSeparated("2.2"); } }; executeTasks(task); } private static void executeTasks(final Callable<Number> task) throws Exception { ExecutorService exec = Executors.newFixedThreadPool(100); List<Future<Number>> results = new ArrayList<Future<Number>>(); for (int i = 0; i < 150; i++) { results.add(exec.submit(task)); } exec.shutdown(); for (Future<Number> result : results) { System.out.println(result.get()); } }Thread safe implementation
I used the most recommended method ThreadLocal to provide a thread based copy of Format object.package aminur.test.formatters; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.ParseException; public class ThreadSafePriceFormatter { // the price formats private static final ThreadLocal<DecimalFormat> pointSeparatedFormat = new ThreadLocal<DecimalFormat>() { @Override protected DecimalFormat initialValue() { DecimalFormatSymbols symbols = new DecimalFormatSymbols(); symbols.setDecimalSeparator('.'); return (new DecimalFormat("#0.00", symbols)); } }; private ThreadSafePriceFormatter() { } public static String formatPointSeparated(double price) { return pointSeparatedFormat.get().format(price); } public static Number parsePointSeparated(String price) throws ParseException { return pointSeparatedFormat.get().parse(price); } }Java Specialist, Dr. Heinz M. Kabutz suggests using ThreadLocal with SoftReference. Some have also suggested to use Joda-Time
Related posts
Feed
- India
- World
- Live
- Cricket News
To find out more, including how to control cookies, see here Cookie Policy