Backend Development 11 min read

Thread Safety Issues of SimpleDateFormat.parse() and format() Methods and Their Solutions

This article explains why SimpleDateFormat.parse() and SimpleDateFormat.format() are not thread‑safe in Java, analyzes the underlying causes involving shared Calendar objects, and presents three practical solutions—including per‑thread instances, synchronized blocks, and ThreadLocal usage—to eliminate concurrency bugs.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Thread Safety Issues of SimpleDateFormat.parse() and format() Methods and Their Solutions

1. Thread Safety Problems of SimpleDateFormat.parse()

1.1 Incorrect Example

The following code uses a static SimpleDateFormat instance in multiple threads, which can cause NumberFormatException due to race conditions:

import java.text.SimpleDateFormat;

public class SimpleDateFormatTest {
    private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public static void main(String[] args) {
        for (int i = 0; i < 20; ++i) {
            Thread thread = new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + "--" +
                        SIMPLE_DATE_FORMAT.parse("2020-06-01 11:35:00"));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "Thread-" + i);
            thread.start();
        }
    }
}

The program throws errors because SimpleDateFormat shares mutable state across threads.

1.2 Cause Analysis

SimpleDateFormat extends DateFormat and relies on a shared Calendar object for both parse and format . The internal Calendar.clear() and other modifications are not synchronized, so when one thread clears or updates the calendar, another thread may read inconsistent data, leading to thread‑safety violations.

1.3 Solutions

Method 1: Create a new SimpleDateFormat per thread

import java.text.SimpleDateFormat;

public class SimpleDateFormatTest {
    public static void main(String[] args) {
        for (int i = 0; i < 20; ++i) {
            Thread thread = new Thread(() -> {
                try {
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    System.out.println(Thread.currentThread().getName() + "--" +
                        sdf.parse("2020-06-01 11:35:00"));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "Thread-" + i);
            thread.start();
        }
    }
}

Method 2: Synchronize access to a shared instance

public class SimpleDateFormatTest {
    private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public static void main(String[] args) {
        for (int i = 0; i < 20; ++i) {
            Thread thread = new Thread(() -> {
                try {
                    synchronized (SIMPLE_DATE_FORMAT) {
                        System.out.println(Thread.currentThread().getName() + "--" +
                            SIMPLE_DATE_FORMAT.parse("2020-06-01 11:35:00"));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "Thread-" + i);
            thread.start();
        }
    }
}

Method 3: Use ThreadLocal to hold a separate SimpleDateFormat for each thread

import java.text.SimpleDateFormat;

public class SimpleDateFormatTest {
    private static final ThreadLocal
SAFE_SDF =
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    public static void main(String[] args) {
        for (int i = 0; i < 20; ++i) {
            Thread thread = new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + "--" +
                        SAFE_SDF.get().parse("2020-06-01 11:35:00"));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "Thread-" + i);
            thread.start();
        }
    }
}

2. Thread Safety Problems of SimpleDateFormat.format()

2.1 Incorrect Example

The following code formats dates using a shared static SimpleDateFormat inside a thread pool, which leads to duplicated or incorrect output:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;

public class SimpleDateFormatTest {
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 10, 60, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(1000));
        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            threadPool.execute(() -> {
                Date date = new Date(finalI * 1000);
                formatAndPrint(date);
            });
        }
        threadPool.shutdown();
    }
    private static void formatAndPrint(Date date) {
        String result = simpleDateFormat.format(date);
        System.out.println("时间:" + result);
    }
}

2.2 Cause Analysis

During formatting, SimpleDateFormat.format() calls calendar.setTime(date) . Because the underlying Calendar is shared, one thread may modify it while another thread is still using it, causing inconsistent results and duplicate timestamps.

2.3 Solutions

The same three approaches from section 1 can be applied: per‑thread instances, synchronized blocks, or ThreadLocal . Each eliminates the shared mutable state and guarantees correct formatting in a concurrent environment.

By adopting any of these patterns, developers can safely use date parsing and formatting utilities in multithreaded Java applications.

JavaConcurrencySynchronizationThread SafetysimpledateformatthreadlocalDate Parsing
Java Architect Essentials
Written by

Java Architect Essentials

Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.

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.