【线程安全】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 的 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();
    }
}

在这里插入图片描述

而后使用 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();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的架狗师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值