Backend Development 7 min read

Why SimpleDateFormat Is Not Thread‑Safe and How to Fix It Efficiently

This article explains why Java's SimpleDateFormat is not thread‑safe, demonstrates the resulting concurrency errors, and presents four practical solutions—including per‑call instantiation, ThreadLocal, Apache FastDateFormat, and Java 8's Instant with DateTimeFormatter—along with JMH benchmark results comparing their performance across JDK 8 and JDK 11.

Java Architecture Diary
Java Architecture Diary
Java Architecture Diary
Why SimpleDateFormat Is Not Thread‑Safe and How to Fix It Efficiently

SimpleDateFormat Thread‑Safety Issue

SimpleDateFormat is not thread‑safe, which can cause parsing errors when used concurrently. The article reproduces a StackOverflow example that creates a static SimpleDateFormat and parses dates in a thread pool, leading to NumberFormatException.

Problematic Code Example

<code>public class ExampleClass {
    private static final Pattern dateCreateP = Pattern.compile("Дата подачи:\\s*(.+)");
    private static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss dd.MM.yyyy");

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(100);
        while (true) {
            executor.submit(() -> workConcurrently());
        }
    }

    public static void workConcurrently() {
        Matcher matcher = dateCreateP.matcher("Дата подачи: 19:30:55 03.05.2015");
        Timestamp startAdvDate = null;
        try {
            if (matcher.find()) {
                String dateCreate = matcher.group(1);
                startAdvDate = new Timestamp(sdf.parse(dateCreate).getTime());
            }
        } catch (Throwable th) {
            th.printStackTrace();
        }
        System.out.print("OK ");
    }
}
</code>

The execution prints “OK” repeatedly but eventually throws a NumberFormatException because the shared SimpleDateFormat instance is accessed by multiple threads.

Solutions

1. Create a new SimpleDateFormat for each use

Instantiate SimpleDateFormat inside the method so each thread gets its own instance.

2. Use ThreadLocal

<code>public class DateUtil {
    private static final ThreadLocal<SimpleDateFormat> local =
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

    public static String format(Date date) {
        return local.get().format(date);
    }

    public static Date parse(String dateStr) throws ParseException {
        return local.get().parse(dateStr);
    }
}
</code>

3. Use Apache Commons Lang 3 FastDateFormat

<code>&lt;dependency&gt;
    &lt;groupId&gt;org.apache.commons&lt;/groupId&gt;
    &lt;artifactId&gt;commons-lang3&lt;/artifactId&gt;
    &lt;version&gt;${commons-lang3-version}&lt;/version&gt;
&lt;/dependency&gt;
</code>

4. Use Instant with DateTimeFormatter (Java 8+)

<code>public static final DateTimeFormatter DATETIME_FORMATTER =
    DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());

public String format(Date date) {
    return DATETIME_FORMATTER.format(date.toInstant());
}
</code>

When formatting an Instant, the time‑zone must be specified.

Performance Comparison (JMH)

JDK 8

<code># JMH version: 1.21
# VM version: JDK 1.8.0_221, Java HotSpot(TM) 64-Bit Server VM, 25.221-b11

Benchmark             Mode  Cnt       Score       Error  Units
newSimpleDateFormat   thrpt    5  114072.841 ±   989.135  ops/s
threadLocal           thrpt    5  348207.331 ± 46014.175  ops/s
fastDateFormat        thrpt    5  434391.553 ±  7799.593  ops/s
</code>

JDK 11

<code># JMH version: 1.21
# VM version: JDK 11.0.4, OpenJDK 64-Bit Server VM, 11.0.4+10-b304.69

Benchmark         Mode  Cnt       Score      Error  Units
fastDateFormat   thrpt    5  384637.138   7402.690  ops/s
toInstantFormat  thrpt    5  487482.436 12490.986  ops/s
</code>

FastDateFormat scores highest on JDK 8, while the Instant + DateTimeFormatter approach outperforms it on JDK 11. On modern JDK versions the built‑in formatter is competitive, making ThreadLocal or Instant‑based solutions viable choices.

Conclusion

For Java 8 and later, using

DateTimeFormatter

with

Instant

provides a thread‑safe and high‑performance alternative to

SimpleDateFormat

. If you must stay on older JDKs,

ThreadLocal&lt;SimpleDateFormat&gt;

or

FastDateFormat

from Apache Commons are reliable options.

performanceThread Safetydateformatsimpledateformatthreadlocal
Java Architecture Diary
Written by

Java Architecture Diary

Committed to sharing original, high‑quality technical articles; no fluff or promotional content.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.