Java8中DateTimeFormatter真的是线程安全的吗?

这篇具有很好参考价值的文章主要介绍了Java8中DateTimeFormatter真的是线程安全的吗?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Java8中DateTimeFormatter真的是线程安全的吗?

答案是否定的

1.背景

  由于之前写了一个旷世的ocr的服务,接入了旷世的FaceID的人脸比对的接口,然后就在写代码的过程中就遇到了这个奇怪的bug,旷世的FaceId的人脸识别的接口文档如下:

https://faceid.com/document/faceid-guide-docs/v5_get_result

  说实话,旷世的产品真的是太难用了,光说这个接口接入后端也没有一个像样的SDK还得自己去写http各种封装接口、参数和解析返回结果,代码量有点的,用起来不像大厂的产品,都是给一个SDK,给使用者降低了接入的门槛有降低、接入的效率有提高和产品的质量也是没有啥缺陷的,相关的ocr的产品比如说是识别新能源和油车的车牌驾驶证还是行驶证上的车牌新能源比油车多一位就识别不出来,这个问题提给他们,他们也修复不了,以后的版本在修复,还是我们做了业务调整处理了这个识别不了的问题,还有就是副页相关的识别也是识别不出来,比起阿里的ocr来说,只能说是一个天上的一个地下,然后我就写了一段代码如下:

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
String fileName = dtf.format(LocalDateTime.now());

  这段代码是在一个工具类的静态方法中用于解析base64的图片(这里的业务是活体检测返回最好的一张人脸照片)和拉取Oss临时身份证正面的图片URL文件到本地,然后调用旷世的接口需要这两个图片作为参数调用人脸比对的接口,活体检测和人脸比对的大体流程简单的说下:

  1.接口授权认证(接口的加解密和参数的签名验签啥的,appkey,secret,)

  2.前端请求业务后端获取biz_token,然后前端的sdk拿着这个biz_token唤醒相机进行活体检测,活体检测会采集一张人脸的最好的一张base64的图片

  3.最后一步就是去调用业务后端封装的一个活体检测和人脸比对的接口,该接口里面做了两件事情:

  第一点:根据前端传来的biz_token去调用旷世的获取活体检测结果的接口,然后解析返回结果
  第二点:根据前端传来的oss的 身份证(ocr识别传到oss上的临时身份证URL)的参数解析为本地的file2,然后根据第一点返回来的最好了的一张活体检测采集到的最好的一张人脸base64的图片解析为本的文件file2,就是这两个文件的解析,调用了同一个工具类的方法,该方法里面写了上面那段神仙代码,然后生成的file1和file2的名字都会有概率是相同的,然后传到旷世那边,找他们排查对接,他们说我们这边穿过去的这两个文件的MD5的值是一样的,瞬间我就感到了不不可思议,于是乎,疯狂review自己的代码和看旷世的接口文档,调整各种姿势修改代码调试和那边对接,那边排查的结果始终是两个文件的MD5值是相同的,最后在一番修改和调试后发现本地生成的文件的名字是一样的并且把两个文件在解析到本地的文件的MD5的值打印出来了,调用旷世的时候又把两个文件对应的MD5的值打印出来了:

// 打印文件的MD5代码
//DigestUtils.md5Hex() 这个方法是这个包里面的 package org.apache.commons.codec.digest;
FileInputStream fis1 = new FileInputStream(xxx); // xxx是传入的File
String idCardMD5 = DigestUtils.md5Hex(fis1);

Java8中DateTimeFormatter真的是线程安全的吗?

  经过一番调试和观察以后还以为是流没有关闭导致文件被占用两个文件的引用都指向同一个对象的地址导致的或者是两个文件的复制搞错了,结果发现两个文件在生成名字的时候会有一定的打概率生成的是相同的名字,这样两调用的地方就拿到的是同一个文件,也就是两个调用的地方指向了这个名字相同的文件导致最后两个文件的MD5的值一样了,两个文件的MD5的值一样就导致身份证的本地图片解析被活体检测采集到的最好的一张照片覆盖了,这种就相当于活体检测的人脸跟活体检测的人脸比对通过,而不是身份证照片和活体检测采集到的最好的一张人脸图片作比对,跟我们的预期不符合了,我们的预期是只有活体检测结果通过并且人脸比对身份证正面照片和活体检测采集的最好的一张图片比对成功,都是通一个人刷脸的操作而不是登录的ocr身份证认证通过的账号和刷脸不是通一个人的这种操作,给我搞了一下午到晚上9点多,把这个问题记录复盘,也是奇葩问题遇到的多,解决的也是那么酸爽的,废话不多说,接下来看如何解决吧。

2.解决办法

2.1办法一:换姿势或者升级JDK的版本

  升级JDK的版本在这里就不采用这种方式了,采用更换姿势的方式

  换姿势代码如下:

private static final DateTimeFormatter dtf = new DateTimeFormatterBuilder().appendPattern("yyyyMMddHHmmss").appendValue(ChronoField.MILLI_OF_SECOND, 3).toFormatter();
//格式化的地方使用这个全局df来格式化
String fileName = df.format(LocalDateTime.now());

  在网上看到了一篇文章,链接如下:

https://zhuanlan.zhihu.com/p/144372694

  这篇文章说Jdk8 DateTimeFormatter 解析 yyyyMMddHHmmssSSS 有问题,然后我就类比猜测了下:Jdk8 DateTimeFormatter 解析 yyyyMMddHHmmss也是有问题的,结果用上面的姿势证明它确实是有bug的,所以以后在回答和使用这个Jdk8 DateTimeFormatter的时候就不能说这个Jdk8 DateTimeFormatter类一定是线程安全的了,这个例子就是一个很好的坑,这个问题在Jdk9中修复,在jdk9及其以上的版本有没有修复,这个可以去官方找或者,升级下jdk试下就知道了,具体jdk8的DateTimeFormatter在解析yyyyMMddHHmmssSSS和yyyyMMddHHmmss格式的时候为啥会有bug?这个问题就不去深究了,根据上面正确的姿势来看估计跟解析格式的精度或者是缓存啥的有关系的。

2.1办法二:更换文件名称字生成策略

  使用其它方式生成唯一的文件名字,可以使用UUID、美团的Leaf、雪花算法(这个也会重复的,就拿mybatisPlus的id生成器来说,默认使用的是雪花算法,会有一定的重复的,所以需要设置机房id(datacenter-id)和work-id),自定义使用时间戳字符串在加随机字符啥的,或者是自己写个分布式ID生成的算法等等,方法还很多的,这里就不在啰嗦了文章来源地址https://www.toymoban.com/news/detail-437735.html

# mybatisPlus的id生成器来说,默认使用的是雪花算法 防止id生成重复的配置如下:
mybatis-plus:
  mapper-locations: classpath*:/mapper/*.xml
  type-aliases-package: com.xxxx.entity
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    datacenter-id: ${random.int(1,31)}
    worker-id: ${random.int(1,31)}

到了这里,关于Java8中DateTimeFormatter真的是线程安全的吗?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JAVA-- 在Java8 Parallel Stream中如何自定义线程池?

    使用Parallel Stream时,在适当的环境中,通过适当地使用并行度级别,可以在某些情况下获得性能提升。 如果程序创建一个自定义ThreadPool,必须记住调用它的shutdown()方法来避免内存泄漏。 如下代码示例,Parallel Stream并行处理使用的线程池是ForkJoinPool.commonPool(),这个线程池是

    2024年02月09日
    浏览(43)
  • Java使用redis-Redis是并发安全的吗?

    大家都清楚,Redis 是一个开源的高性能键值对存储系统,被开发者广泛应用于缓存、消息队列、排行榜、计数器等场景。 由于其高效的读写性能和丰富的数据类型,Redis 受到了越来越多开发者的青睐。然而,在并发操作下,Redis 是否能够保证数据的一致性和安全性呢?接下来

    2024年02月11日
    浏览(71)
  • Java 21 正式 GA,虚拟线程真的来了

    UTC 时间 2023 年 9 月 19 日,期盼已久的 Java 21 终于发布正式版! 本文一起来看看其中最受 Java 开发者关注的一项新特性:Loom 项目的两个新特性之一的 ”虚拟线程(Virtual Thread)“(另外一个新特性是 ”结构化并发(Structured Concurrency)“,当前是预览状态),它被称之为 J

    2024年02月08日
    浏览(56)
  • Java中DateTimeFormatter的使用方法和案例

    🔔简介 在Java中,DateTimeFormatter类用于格式化和解析日期时间对象。它是日期时间格式化的强大而灵活的工具。 🔔作用 🌵1.本地化时间 本地化时间指根据指定的语言环境显示时间 1.1.创建DateTimeFormatter时指定Locale 1.2.使用该DateTimeFormatter格式化日期时间 1.3.可以通过Locale.US、L

    2024年02月08日
    浏览(31)
  • Java中日期时间格式化方法SimpleDateFormat和DateTimeFormatter使用完整示例及区别说明

    示例代码: 示例截图:  这里完整的用两种方法分别实现了日期和String的来回转换,鉴于SimpleDateFormat早已过时,且非线程安全,所以推荐大家首选使用DateTimeFormatter,用法基本都是差不多的。变化不大。但是DateTimeFormatter需要Java Level 8(8 - Lambdas, type annotations etc.),需留意。

    2023年04月09日
    浏览(40)
  • Java多线程 - 线程安全和线程同步解决线程安全问题

    线程安全问题指的是: 多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,称为线程安全问题。 举例: 取钱模型演示 需求:小明和小红是一对夫妻,他们有一个共同的账户,余额是10万元。 如果小明和小红同时来取钱,而且2人都要取钱10万元,可能出现什么问

    2023年04月15日
    浏览(38)
  • Go 语言 map 是并发安全的吗?

    原文链接: Go 语言 map 是并发安全的吗? Go 语言中的 map 是一个非常常用的数据结构,它允许我们快速地存储和检索键值对。然而,在并发场景下使用 map 时,还是有一些问题需要注意的。 本文将探讨 Go 语言中的 map 是否是并发安全的,并提供三种方案来解决并发问题。 先来

    2024年02月06日
    浏览(36)
  • java基础之线程安全问题以及线程安全集合类

    当多个线程同时访问同一个临界资源时,原子操作可能被破坏,会导致数据丢失, 就会触发线程安全问题 临界资源: 被多个线程同时访问的对象 原子操作: 线程访问临界资源的过程中不可更改和缺失的操作 互斥锁 每个对象都默认拥有互斥锁, 该锁默认不开启. 当开启互斥锁之后

    2024年01月18日
    浏览(56)
  • Java 多线程之线程安全集合

    集合关系图 本文主要关注线程安全的集合,如 List、Set、Queue、Map 等接口的线程安全的实现方式,有关集合基础知识请转到这里。所谓线程安全集合,就是在多线程环境中使用集合不会导致数据不一致和数据异常的集合。在 Java 中线程安全集现在基本都使用 java.util.concurrent

    2024年02月05日
    浏览(48)
  • Java多线程之线程安全问题

    我们知道操作系统中线程程的调度是抢占式执行的, 宏观上上的感知是随机的, 这就导致了多线程在进行线程调度时线程的执行顺序是不确定的, 因此多线程情况下的代码的执行顺序可能就会有无数种, 我们需要保证这无数种线程调度顺序的情况下, 代码的执行结果都是正确的

    2023年04月17日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包