【线程安全】SimpleDateFormat线程安全问题

线上告警群出现告警啦,下面是具体的告警堆栈日志

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:2084)
        at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
        at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
        at java.text.DateFormat.parse(DateFormat.java:364)

一、原因分析:

以上异常的原因是 SimpleDateFormat 是线程不安全的,因为 SimpleDateFormat 是有状态的,如 DateFormat 类中含有非线程安全的 NumberFormat 成员变量:
【线程安全】SimpleDateFormat线程安全问题_第1张图片
从 SimpleDateFormat 的 Doc 中能看到如下描述:
在这里插入图片描述

二、修改方案:

  • 方案一:

    每次都新建 SimpleDateFormat 类使用

  • 方案二:
    针对 SimpleDateFormat 类的线程安全问题,apache commons-lang 包提供了 FastDateFormat 类。
    该修复方案相对来说代码改造量最小,仅需在声明静态 SimpleDateFormat 实例代码处将 SimpleDateFormat 实例替换为 FastDateFormat 实例。
    示例代码如下:

public static final FastDateFormat yyyMMddFormat          = FastDateFormat.getInstance("yyyy-MM-dd");

三、案发现场复现

public class SimpleDateFormatTest {
    private static final AtomicBoolean STOP = new AtomicBoolean();
    private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd"); // 非线程安全

    public static void main(String[] args) {
        Runnable runnable = () -> {
            int count = 0;
            while (!STOP.get()) {
                try {
                    FORMATTER.parse("2025-04-27");
                } catch (Exception e) {
                    e.printStackTrace();
                    if (++count > 3) {
                        STOP.set(true);
                    }
                }
            }
        };

        new Thread(runnable).start();
        new Thread(runnable).start();
    }
}

【线程安全】SimpleDateFormat线程安全问题_第2张图片

而后使用 FastDateFormat 不再报错

public class FastDateFormatTest {
    private static final AtomicBoolean STOP = new AtomicBoolean();
    private static final FastDateFormat FORMATTER = FastDateFormat.getInstance("yyyy-MM-dd"); // 线程安全

    public static void main(String[] args) {
        Runnable runnable = () -> {
            int count = 0;
            while (!STOP.get()) {
                try {
                    FORMATTER.parse("2023-7-15");
                } catch (Exception e) {
                    e.printStackTrace();
                    if (++count > 3) {
                        STOP.set(true);
                    }
                }
            }
        };

        new Thread(runnable).start();
        new Thread(runnable).start();
    }
}

你可能感兴趣的:(日常踩坑,安全)