Verify SimpleDateFormat is not thread safe 2018-11-15 05:38
Don't use SimpleDateFormat
in multi-threaded scenario. It is not thread-safe class. Here is the code to verify it. I start 10 threads to parse string to date with the same instance of SimpleDateFormat
.
public class HandleDate {
// this is not thread safe
private SimpleDateFormat simpleDateFormatTimestampJPOS = new SimpleDateFormat("yyyyMMddHHmmssSSS");
public void verifyNotThreadSafe(String timeStamp) {
try {
// this is thread safe
SimpleDateFormat simpleDateFormatTimestampJPOS = new SimpleDateFormat("yyyyMMddHHmmssSSS");
Date date = simpleDateFormatTimestampJPOS.parse(timeStamp);
LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
String expectedYear = timeStamp.substring(0, 4);
String actualYear = String.valueOf(localDateTime.getYear());
if (!expectedYear.equals(actualYear)) {
System.out.println("expected:" + expectedYear + ", but real:" + actualYear);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class MultiThreadSimpleDateFormatClient {
public static void main(String[] args) {
HandleDate handleDate = new HandleDate();
Random random = new Random();
Set<String> randomStrs = new HashSet<>();
Thread thread1 = new Thread(() -> {
while (true) {
int partOfYear = random.nextInt(10);
handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
}
});
Thread thread2 = new Thread(() -> {
while (true) {
int partOfYear = random.nextInt(10);
handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
}
});
Thread thread3 = new Thread(() -> {
while (true) {
int partOfYear = random.nextInt(10);
handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
}
});
Thread thread4 = new Thread(() -> {
while (true) {
int partOfYear = random.nextInt(10);
handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
}
});
Thread thread5 = new Thread(() -> {
while (true) {
int partOfYear = random.nextInt(10);
handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
}
});
Thread thread6 = new Thread(() -> {
while (true) {
int partOfYear = random.nextInt(10);
handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
}
});
Thread thread7 = new Thread(() -> {
while (true) {
int partOfYear = random.nextInt(10);
handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
}
});
Thread thread8 = new Thread(() -> {
while (true) {
int partOfYear = random.nextInt(10);
handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
}
});
Thread thread9 = new Thread(() -> {
while (true) {
int partOfYear = random.nextInt(10);
handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
}
});
Thread thread10 = new Thread(() -> {
while (true) {
int partOfYear = random.nextInt(10);
handleDate.verifyNotThreadSafe("201" + partOfYear + "1115040613555");
}
});
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
thread6.start();
thread7.start();
thread8.start();
thread9.start();
thread10.start();
}
}
Part of output is here
expected:2013, but real:7
expected:2014, but real:1514
expected:2017, but real:15
expected:2012, but real:2014
expected:2014, but real:555
expected:2018, but real:1
expected:2012, but real:1
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:601)
at java.lang.Long.parseLong(Long.java:631)
at java.text.DigitList.getLong(DigitList.java:195)
at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2160)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.henryxi.core.text.HandleDate.verifyNotThreadSafe(HandleDate.java:16)
at com.henryxi.core.text.MultiThreadSimpleDateFormatClient.lambda$main$9(MultiThreadSimpleDateFormatClient.java:71)
at java.lang.Thread.run(Thread.java:745)
java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:601)
at java.lang.Long.parseLong(Long.java:631)
at java.text.DigitList.getLong(DigitList.java:195)
at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2160)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.henryxi.core.text.HandleDate.verifyNotThreadSafe(HandleDate.java:16)
at com.henryxi.core.text.MultiThreadSimpleDateFormatClient.lambda$main$6(MultiThreadSimpleDateFormatClient.java:53)
at java.lang.Thread.run(Thread.java:745)
EOF