Why SimpleDateFormat Breaks in Multithreaded Java and How to Fix It
This article explains why the classic Java SimpleDateFormat class is not thread‑safe, demonstrates the problems caused by using a static instance in concurrent code, and presents four practical solutions—including creating new instances, synchronizing, using ThreadLocal, and switching to the immutable DateTimeFormatter.
Introduction
In everyday development we often use time‑related classes, and SimpleDateFormat is familiar for formatting and parsing dates. However, SimpleDateFormat is not thread‑safe, which leads to exceptions in multithreaded environments.
Problem Scenario Reproduction
Typical code defines SimpleDateFormat as a static variable to avoid frequent object creation:
public class SimpleDateFormatTest {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String formatDate(Date date) throws ParseException {
return sdf.format(date);
}
public static Date parse(String strDate) throws ParseException {
return sdf.parse(strDate);
}
public static void main(String[] args) throws InterruptedException, ParseException {
System.out.println(sdf.format(new Date()));
}
}Running this code in a single thread works fine, but under multiple threads it produces incorrect results and sometimes throws NumberFormatException or crashes.
Why SimpleDateFormat Is Not Thread‑Safe
Because the static SimpleDateFormat instance is shared, its internal Calendar field is also shared. One thread may set the calendar time while another thread reads it, causing date mismatches and exceptions. The source code of format() shows it operates on a shared Calendar object.
private StringBuffer format(Date date, StringBuffer toAppendTo) {
calendar.setTime(date);
// ... further processing ...
return toAppendTo;
}The Alibaba Java Development Manual explicitly warns that date formats are not synchronized and recommends creating separate format instances for each thread or synchronizing external access.
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.
Solutions
Create a New Instance When Needed
public static String formatDate(Date date) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
public static Date parse(String strDate) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.parse(strDate);
}This eliminates shared state but incurs object‑creation overhead.
Synchronize Access
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String formatDate(Date date) throws ParseException {
synchronized (sdf) {
return sdf.format(date);
}
}
public static Date parse(String strDate) throws ParseException {
synchronized (sdf) {
return sdf.parse(strDate);
}
}Synchronizing guarantees correctness but may become a bottleneck under high concurrency.
ThreadLocal
private static final ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static Date parse(String dateStr) throws ParseException {
return threadLocal.get().parse(dateStr);
}
public static String format(Date date) {
return threadLocal.get().format(date);
}Each thread gets its own SimpleDateFormat instance, removing contention.
Use Java 8 DateTimeFormatter
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static String formatDate2(LocalDateTime date) {
return formatter.format(date);
}
public static LocalDateTime parse2(String dateNow) {
return LocalDateTime.parse(dateNow, formatter);
}DateTimeFormatter is immutable and thread‑safe, providing the most efficient solution.
All four approaches eliminate the thread‑safety issue demonstrated earlier.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
