Joe
Joe

Reputation: 15349

Can we declare SimpleDateFormat objects as static objects

SimpleDateFormat monthFormat = new SimpleDateFormat("MMMM");
SimpleDateFormat fullFormat = new SimpleDateFormat("EE MMM dd, HH:mm:ss")

I have several such piece of code which gets invoked often, would it make sense to declare them as static variables?

Is it thread safe to pass dynamic arguments to the format() method in such cases?

Upvotes: 23

Views: 20690

Answers (6)

Ankur Singhal
Ankur Singhal

Reputation: 26077

SimpleDateFormat is simple in use, but yet complicated. Developer generally writes code and test as a single user, and succeeds. but in case of parallel and concurrent requests,SimpleDateFormat is a complicated dateformat.

having exposed as static in service or controller layer, and when multiple threads can access at same time, it can behave abnornally.

DateFormat APi class also recommends, "It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally."

code to demontsrate it's failure in multi threading environment.

public final class DateUtils {

    public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");

    private DateUtils() {
    }

    public static Date parse(String target) throws ParseException {
        return DATE_FORMAT.parse(target);
    }

    public static String format(Date target) {
        return DATE_FORMAT.format(target);
    }

    private static void testSimpleDateFormatWithThreads() {
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        final String source = "2019-01-11";

        IntStream.rangeClosed(0, 20)
                .forEach((i) -> executorService.submit(() -> {
                    try {
                        System.out.println(DateUtils.parse(source));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                }));

        executorService.shutdown();
    }

    public static void main(String[] args) {
        testSimpleDateFormatWithThreads();
    }
}

Output - This varies from machine to machine and will generate difefrent output how many time you run.

Wed Nov 11 00:00:00 IST 2201

Wed Nov 11 00:00:00 IST 2201

Fri Jan 11 00:00:00 IST 2019

Fri Jan 11 00:00:00 IST 2019

Fri Jan 11 00:00:00 IST 2019

Fri Jan 11 00:00:00 IST 2019

Fri Jan 11 00:00:00 IST 2019

Mon Dec 31 00:00:00 IST 2018

Fri Jan 11 00:00:00 IST 2019

Fri Jan 11 00:00:00 IST 2019

Fri Jan 11 00:00:00 IST 2019

Fri Jan 11 00:00:00 IST 2019

Fri Jan 11 00:00:00 IST 2019

Fri Jan 11 00:00:00 IST 2019

Fri Jan 11 00:00:00 IST 2019

Fri Jan 11 00:00:00 IST 2019

Fri Jan 11 00:00:00 IST 2019

Fri Jan 11 00:00:00 IST 2019

Sun Jan 11 00:00:00 IST 2201

Fri Jan 11 00:00:00 IST 2019

This breaks due to Calendar instance variable inside DateFormat -> SimpleDateFormat class.

Solution

  1. Using ThreadLocal

    public static final ThreadLocal SIMPLE_DATE_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

  2. Java 8, DateTimeFormatter API

    public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

Upvotes: 0

Magnilex
Magnilex

Reputation: 11978

As of Java 8, this is supported in the new Date API. DateTimeFormatter is thread-safe and can do the same work as SimpleDateFormat. Cited from the JavaDoc:

A formatter created from a pattern can be used as many times as necessary, it is immutable and is thread-safe.

To be extra clear, it is perfectly fine to define a format such as:

private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");

And use it in methods that can be accessed by several threads concurrently:

String text = date.toString(formatter);
LocalDate date = LocalDate.parse(text, formatter);

Upvotes: 26

ledlogic
ledlogic

Reputation: 850

An alternative if you are already using Apache Commons:

https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/time/FastDateFormat.html

Upvotes: 2

dogbane
dogbane

Reputation: 274738

DateFormat is not thread-safe. If multiple threads use the same DateFormat object without any synchronization you can get unexpected results. So you should either synchronize access to the DateFormat object, use a ThreadLocal variable or use an alternative Date API such as Joda-Time.

For more information on how to do this, take a look at this blog post: DateFormat with Multiple Threads

Upvotes: 2

Jigar Joshi
Jigar Joshi

Reputation: 240928

No they aren't thread-safe.Use Joda-time's version instead.

Or make them wrapped in synchronized method and make it thread-safe

Doc Says it clearly

Date formats are 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.

Upvotes: 31

Jens Schauder
Jens Schauder

Reputation: 81970

static shouldn't be a problem.

Since AFAIK no guarantees are made about thread safety you'd have to check the source code for that. And even if you come to the conclusion that it is thread safe, this might change with the next release. As said in another answer they are not thread safe.

Do you really allocate such a huge amount of threads that giving each thread its own Format is a problem?

Upvotes: -2

Related Questions