java8+ 简单、安全、高效的格式化 Date

简介: SimpleDateFormat 线程不安全众所周知 SimpleDateFormat 线程不安全,不少朋友被其坑过。下面是 stackoverflow 的文章 why-is-javas-simpledateformat-not-thread-safe 中的栗子。

SimpleDateFormat 线程不安全

众所周知 SimpleDateFormat 线程不安全,不少朋友被其坑过。

下面是 stackoverflow 的文章 why-is-javas-simpledateformat-not-thread-safe 中的栗子。

public class ExampleClass {

    private static final Pattern dateCreateP = Pattern.compile("Дата подачи:\\s*(.+)");
    private static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss dd.MM.yyyy");

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(100);
        while (true) {
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    workConcurrently();
                }
            });
        }
    }

    public static void workConcurrently() {
        Matcher matcher = dateCreateP.matcher("Дата подачи: 19:30:55 03.05.2015");
        Timestamp startAdvDate = null;
        try {
            if (matcher.find()) {
                String dateCreate = matcher.group(1);
                startAdvDate = new Timestamp(sdf.parse(dateCreate).getTime());
            }
        } catch (Throwable th) {
            th.printStackTrace();
        }
        System.out.print("OK ");
    }
}

And result :

OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK java.lang.NumberFormatException: For input string: ".201519E.2015192E2"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at java.text.DigitList.getDouble(DigitList.java:169)
at java.text.DecimalFormat.parse(DecimalFormat.java:2056)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.nonscalper.webscraper.processor.av.ExampleClass.workConcurrently(ExampleClass.java:37)
at com.nonscalper.webscraper.processor.av.ExampleClass$1.run(ExampleClass.java:25)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

解决方案

  1. 每次 new (实例化) SimpleDateFormat
  2. 利用 ThreadLocal 确保每个线程都可以得到单独的一个 SimpleDateFormat
public class DateUtil {
    private static final ThreadLocal<SimpleDateFormat> local = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

    public static String format(Date date) {
        return local.get().format(date);
    }

    public static Date parse(String dateStr) throws ParseException {
        return local.get().parse(dateStr);
    }
}
  1. commons-lang3 中的 FastDateFormat
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>${commons-lang3-version}</version>
</dependency>

性能比拼

性能咋样,jmh 来一把,源码见:https://github.com/lets-mica/mica-jmh

# JMH version: 1.21
# VM version: JDK 1.8.0_221, Java HotSpot(TM) 64-Bit Server VM, 25.221-b11

Benchmark             Mode  Cnt       Score       Error  Units
newSimpleDateFormat  thrpt    5  114072.841 ±   989.135  ops/s
threadLocal          thrpt    5  348207.331 ± 46014.175  ops/s
fastDateFormat       thrpt    5  434391.553 ±  7799.593  ops/s

结果:fastDateFormat 得分最高。当然你觉得这样就完了?

利用 Instant + DateTimeFormatter

mica 1.2.1 中我们利用 Instant 来中转 Date 使用 DateTimeFormatter 格式化。

public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());

public String format(Date date) {
    return DATETIME_FORMATTER.format(date.toInstant());
}

注意:DateTimeFormatter 格式化 Instant 需要指定时区

jdk 8 压测结果

# JMH version: 1.21
# VM version: JDK 1.8.0_221, Java HotSpot(TM) 64-Bit Server VM, 25.221-b11

Benchmark         Mode  Cnt       Score      Error  Units
fastDateFormat   thrpt    5  417338.980  56543.104  ops/s
toInstantFormat  thrpt    5  371028.709  72059.917  ops/s

jdk 11 压测结果

# JMH version: 1.21
# VM version: JDK 11.0.4, OpenJDK 64-Bit Server VM, 11.0.4+10-b304.69

Benchmark         Mode  Cnt       Score      Error  Units
fastDateFormat   thrpt    5  384637.138   7402.690  ops/s
toInstantFormat  thrpt    5  487482.436  12490.986  ops/s

结论

使用 DateTimeFormatter + Instantjava8 下和 commons-lang3 中的 FastDateFormat 已经接近 ,高版本的 jdk 表现突出。
如果你在使用高版本的 jdk 或者考虑后期升级到高版本的 JDK,该方式都是一个不错的选择。

欢迎关注我们的公众号:如梦技术,精彩内容每日推送。

目录
相关文章
|
1月前
|
Java 关系型数据库 MySQL
37、一篇文章学习 Java 中的日期相关类(Date 和 Calendar),非常常用
37、一篇文章学习 Java 中的日期相关类(Date 和 Calendar),非常常用
27 0
|
3月前
|
Java
关于java获取时间 new Date(),显示“上午、下午”
关于java获取时间 new Date(),显示“上午、下午”
36 0
|
3月前
|
安全 Java
JAVA 线程安全
【1月更文挑战第4天】JAVA 线程安全
|
4月前
|
监控 安全 数据可视化
【Java】UWB高精度工业人员安全定位系统源码
【Java】UWB高精度工业人员安全定位系统源码
71 0
|
7天前
|
存储 安全 Java
Java中的容器,线程安全和线程不安全
Java中的容器,线程安全和线程不安全
15 1
|
9天前
|
SQL 安全 Java
Java安全编程:防范网络攻击与漏洞
【4月更文挑战第15天】本文强调了Java安全编程的重要性,包括提高系统安全性、降低维护成本和提升用户体验。针对网络攻击和漏洞,提出了防范措施:使用PreparedStatement防SQL注入,过滤和转义用户输入抵御XSS攻击,添加令牌对抗CSRF,限制文件上传类型和大小以防止恶意文件,避免原生序列化并确保数据完整性。及时更新和修复漏洞是关键。程序员应遵循安全编程规范,保障系统安全。
|
26天前
|
存储 安全 Java
【Java技术专题】「攻破技术盲区」攻破Java技术盲点之unsafe类的使用指南(打破Java的安全管控— sun.misc.unsafe)
【Java技术专题】「攻破技术盲区」攻破Java技术盲点之unsafe类的使用指南(打破Java的安全管控— sun.misc.unsafe)
33 0
|
4月前
|
存储 安全 Java
Java并发编程学习4-线程封闭和安全发布
本篇介绍 对象的共享之线程封闭和安全发布
62 2
Java并发编程学习4-线程封闭和安全发布
|
5月前
|
XML Java API
Java实现XML格式化
Java实现XML格式化
118 0
|
2月前
|
存储 安全 算法
Java泛型与集合:类型安全的集合操作实践
Java泛型与集合:类型安全的集合操作实践