Why not to Use SimpleDateFormat class in multithreaded environment ?

As we all know SimpleDateFormat class is used to format and parse dates in Java with patterns like yyyy-MM-dd HH:mm:ss, and then use that instance to format and parse dates to or from string.

SimpleDateFormat class is that it is not thread-safe and causes issues in multi-threaded environments if not used properly.

Example of Thread Safety problem in SimpleDateFormat

Let’s understand what happens when we try to use SimpleDateFormat in a multi-threaded environment without any synchronization.

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SimpleDateFormatThreadExample {

    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

    public static void main(String[] args) {
        String dateStr = "2015-07-22T10:00:00";

        ExecutorService executorService = Executors.newFixedThreadPool(10);
        Runnable task = new Runnable() {
            @Override
            public void run() {
                parseDate(dateStr);
            }
        };

        for(int i = 0; i < 100; i++) {
            executorService.submit(task);
        }

        executorService.shutdown();
    }

    private static void parseDate(String dateStr) {
        try {
            Date date = simpleDateFormat.parse(dateStr);
            System.out.println("Successfully Parsed Date " + date);
        } catch (ParseException e) {
            System.out.println("ParseError " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

We have a single instance of SimpleDateFormat that we use to parse dates from multiple threads.

it will produce an output like this –

# Output
Successfully Parsed Date Fri Jul 22 10:00:00 IST 2015
Successfully Parsed Date Thu Jul 22 10:00:00 IST 2015
java.lang.NumberFormatException: multiple points
        at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890)
        at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
        at java.base/java.lang.Double.parseDouble(Double.java:543)
        at java.base/java.text.DigitList.getDouble(DigitList.java:169)
        at java.base/java.text.DecimalFormat.parse(DecimalFormat.java:2098)
        at java.base/java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1915)
        at java.base/java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1529)
        at java.base/java.text.DateFormat.parse(DateFormat.java:386)
        at SimpleDateFormatThreadExample.parseDate(SimpleDateFormatThreadExample.java:32)
        at SimpleDateFormatThreadExample.access$000(SimpleDateFormatThreadExample.java:8)
        at SimpleDateFormatThreadExample$1.run(SimpleDateFormatThreadExample.java:19)
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:514)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.base/java.lang.Thread.run(Thread.java:844)
java.lang.NumberFormatException: For input string: ""
        at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
        at java.base/java.lang.Long.parseLong(Long.java:702)
        at java.base/java.lang.Long.parseLong(Long.java:817)
        at java.base/java.text.DigitList.getLong(DigitList.java:195)
        at java.base/java.text.DecimalFormat.parse(DecimalFormat.java:2093)

This class mutates its internal state for formatting and parsing dates. That’s why it results in these issues when multiple threads use the same instance of SimpleDateFormat concurrently.

How to  use SimpleDateFormat in a multi-threaded environment?

  • Create a new instance of SimpleDateFormat for each thread.
  • Synchronize concurrent access by multiple threads with a synchronized keyword or a lock.

1. Create separate instances of SimpleDateFormat for every thread

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SimpleDateFormatThreadExample {

    public static void main(String[] args) {
        String dateStr = "2015-07-22T10:00:00";

        ExecutorService executorService = Executors.newFixedThreadPool(10);
        Runnable task = new Runnable() {
            @Override
            public void run() {
                parseDate(dateStr);
            }
        };

        for(int i = 0; i < 100; i++) {
            executorService.submit(task);
        }

        executorService.shutdown();
    }

    private static void parseDate(String dateStr) {
        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
            Date date = simpleDateFormat.parse(dateStr);
            System.out.println("Successfully Parsed Date " + date);
        } catch (ParseException e) {
            System.out.println("ParseError " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

The output of the above program won’t have any errors –

# Output
Successfully Parsed Date Fri Jun 22 10:00:00 IST 2018
Successfully Parsed Date Fri Jun 22 10:00:00 IST 2018
Successfully Parsed Date Fri Jun 22 10:00:00 IST 2018
Successfully Parsed Date Fri Jun 22 10:00:00 IST 2018
Successfully Parsed Date Fri Jun 22 10:00:00 IST 2018
Successfully Parsed Date Fri Jun 22 10:00:00 IST 2018
Successfully Parsed Date Fri Jun 22 10:00:00 IST 2018
Successfully Parsed Date Fri Jun 22 10:00:00 IST 2018
Successfully Parsed Date Fri Jun 22 10:00:00 IST 2018

2.Share the same instance of SimpleDateFormat but synchronize concurrent access


import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SimpleDateFormatThreadExample {

    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

    public static void main(String[] args) {
        String dateStr = "2015-07-22T10:00:00";

        ExecutorService executorService = Executors.newFixedThreadPool(10);
        Runnable task = new Runnable() {
            @Override
            public void run() {
                parseDate(dateStr);
            }
        };

        for(int i = 0; i < 100; i++) {
            executorService.submit(task);
        }

        executorService.shutdown();
    }

    private synchronized static void parseDate(String dateStr) {
        try {
            Date date = simpleDateFormat.parse(dateStr);
            System.out.println("Successfully Parsed Date " + date);
        } catch (ParseException e) {
            System.out.println("ParseError " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Java 8 has a better and more enhanced DateTimeFormatter which is also thread-safe.

Leave a Reply

Your email address will not be published. Required fields are marked *