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

文章目录

    • @[toc]
  • 1.背景
  • 2.解决办法
    • 2.1办法一:换姿势或者升级JDK的版本
    • 2.1办法二:更换文件名称字生成策略

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);

在这里插入图片描述

  经过一番调试和观察以后还以为是流没有关闭导致文件被占用两个文件的引用都指向同一个对象的地址导致的或者是两个文件的复制搞错了,结果发现两个文件在生成名字的时候会有一定的打概率生成的是相同的名字,这样两调用的地方就拿到的是同一个文件,也就是两个调用的地方指向了这个名字相同的文件导致最后两个文件的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生成的算法等等,方法还很多的,这里就不在啰嗦了

# 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)}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/17201.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++笔记——第十六篇 异常

目录 1.C语言传统的处理错误的方式 2. C异常概念 3. 异常的使用 3.1 异常的抛出和捕获 在函数调用链中异常栈展开匹配原则 3.2异常安全 4.异常的优缺点 1.C语言传统的处理错误的方式 传统的错误处理机制: 1. 终止程序,如assert,缺陷&a…

04-Vue技术栈之组件化编程

目录 1、模块与组件、模块化与组件化1.1 模块1.2 组件1.3 模块化1.4 组件化1.5 传统方式编写应用1.6 组件方式编写应用 2、非单文件组件2.1 基本使用2.2 几个注意点2.3 组件的嵌套2.4 VueComponent2.5 一个重要的内置关系2.6 总结 3、单文件组件3.1 一个.vue 文件的组成(3 个部…

【玩转Git三剑客笔记】第一章 Git基础

第一章 Git基础 1.综述2.安装Git3.使用Git之前需要做的最小配置4.创建第一个仓库并配置local用户信息1.创建Git仓库2.设置Git最小配置 5.通过几次commit来认识工作区和暂存区1.将工作区中所有已经被git追踪的文件一起添加到暂存区2.git log查看提交日志 6.给文件重命名的简便方…

权限提升:不带引号服务路径 || 不安全的服务权限.

权限提升:不带引号服务路径 || 不安全的服务权限. 权限提升简称提权,由于操作系统都是多用户操作系统,用户之间都有权限控制,比如通过 Web 漏洞拿到的是 Web 进程的权限,往往 Web 服务都是以一个权限很低的账号启动…

探讨Redis缓存问题及解决方案:缓存穿透、缓存击穿、缓存雪崩与缓存预热(如何解决Redis缓存中的常见问题并提高应用性能)

Redis是一种非常流行的开源缓存系统,用于缓存数据以提高应用程序性能。但是,如果我们不注意一些缓存问题,Redis也可能会导致一些性能问题。在本文中,我们将探讨Redis中的一些常见缓存问题,并提供解决方案。 一、缓存穿…

了解MSIL汇编和IL汇编评估堆栈

.assembly extern mscorlib {}.assembly Test{.ver 1:0:1:0}.module test.exe.method static void main() cil managed{.maxstack 1.entrypointldstr "I am from the IL Assembly Language..."call void [mscorlib]System.Console::WriteLine (string)ret} 这是MSIL…

1、Flutter使用总结(RichText、Container)

1、创建Flutter项目 flutter create DemoName 2、运行项目 flutter run -d ‘iPhone 14 Pro Max’ 注: 当运用Android Studio时、选择安卓模拟器运行项目、如果项目路径有中文名称: 那么运行报错、如果直接在项目路径下,采用终端运行安卓模拟器、可执行如下命令 flutter ru…

C语言复习笔记2

1.变量命名只能以数字、字母、下划线组成并且不能以数字开头。 #include<stdio.h> #include<unistd.h>//变量名只能由数字字母下划线组成&#xff0c;不能以数字开头 int main() {//int 2b;return 0; }2.内存中保存的是补码 0的补码取反得补码再求源码是-1。 源码…

(8) 支持向量机分类器SVC案例:预测明天是否会下雨

文章目录 案例介绍1 导库导数据&#xff0c;探索特征2 分集&#xff0c;优先探索标签3 探索特征&#xff0c;开始处理特征矩阵3.1 描述性统计与异常值3.2 处理困难特征&#xff1a;日期3.3 处理困难特征&#xff1a;地点3.4 处理分类型变量&#xff1a;缺失值3.5 处理分类型变量…

敏捷ACP.敏捷估计与规划.Mike Cohn.

第一部分 传统规划失败的原因 vs 敏捷规划有效的原因 要回答一个 新产品的范围/进度/资源的组合问题&#xff0c;传统规划过程一般不会产生令人非常满意的答案和最终产品。以下- -些论据可以支持这个结论: ●大约2/3的项目会显著超出费用预算(LedererandPrasad1992) ●产…

网络编程 总结一

一、网络基础&#xff1a; 概念&#xff1a;1> 网络编程的本质就是进程间的通信&#xff0c;只不过进程分布在不同的主机上 2>在跨主机传输过程中&#xff0c;需要确定通信协议后&#xff0c;才可以通信 1. OSI体系结构&#xff08;重点&#xff09; 定义7层模型&…

Vue电商项目--vuex模块开发

vuex状态管理库 vuex是什么&#xff1f; vuex是官方提供的一个插件&#xff0c;状态管理库&#xff0c;集中式管理项目中组件共有的数据。 切记&#xff0c;并不是全部的项目都需要Vuex,如果项目很小&#xff0c;完全不需要vuex,如果项目很大&#xff0c;组件很多&#xff0…

【Leetcode -142.环形链表Ⅱ -143.重排链表】

Leetcode Leetcode -142.环形链表ⅡLeetcode - 143.重排链表 Leetcode -142.环形链表Ⅱ 题目&#xff1a;给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 …

Spring源码解读——高频面试题

Spring IoC的底层实现 1.先通过createBeanFactory创建出一个Bean工厂&#xff08;DefaultListableBeanFactory&#xff09; 2.开始循环创建对象&#xff0c;因为容器中的bean默认都是单例的&#xff0c;所以优先通过getBean、doGetBean从容器中查找&#xff0c;如果找不到的…

QML状态与过渡(States and Transitions)

目录 一 状态&#xff08;States&#xff09; 一 过渡&#xff08;Transitions&#xff09; 通常我们将用户界面描述为一种状态。一个状态定义了一组属性的改变&#xff0c;并且会在一定的条件下被触发。另外在这些状态转化的过程中可以有一个过渡&#xff0c;定义了这些属性…

SpringBoot+vue文件上传下载预览大文件分片上传文件上传进度

文章目录 学习链接上传文件前端后端代码 下载文件a标签下载前端代码后台代码 动态a标签下载前端代码 axios 动态a标签前端代码 浏览器直接输入 预览文件前端代码后端代码 分片上传前后端分别md5加密spark-md5commons-codec 分片上传实现1前端代码后端代码 分片上传实现2前端代…

Spark RDD 持久化(CheckPoint 检查点)

RDD Cache 缓存 RDD 通过 Cache 或者 Persist 方法将前面的计算结果缓存&#xff0c;默认情况下会把数据以缓存 在 JVM 的堆内存中。但是并不是这两个方法被调用时立即缓存&#xff0c;而是触发后面的 action 算 子时&#xff0c;该 RDD 将会被缓存在计算节点的内存中 // cach…

常用排序算法汇总—Python版

一、选择排序 1. 原理&#xff1a; 选择排序&#xff08;Selection Sort&#xff09;是一种简单直观的排序算法&#xff0c;它的基本思路是将数组按顺序分成已排序部分和未排序部分&#xff0c;然后每次从未排序部分中选择出最小的元素&#xff0c;将其添加到已排序部分的末尾…

【五一创作】【软考:软件设计师】 5 计算机组成与体系结构(三)认证技术 | 计算机可靠性

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 本文收录于软考中级&#xff1a;软件设计师系列专栏,本专栏服务于软考中级的软件设计师考试,包括不限于知识点讲解与真题讲解两大部分,并且提供电子教材与电子版真题,关注私聊即可 …

三范式(详解+例子)

第一范式&#xff08;1NF&#xff09;&#xff1a;每一列都是不可分割的原子数据项&#xff08;什么意思&#xff0c;每一项都不可分割&#xff0c;像下面的表格就能分割&#xff0c;所以它连第一范式都算不上&#xff09; 分割后的样子 &#xff08;它就是第一范式了&#xff…
最新文章