分析非线程安全SimpleDateFormat以及使用改进优化方案

SimpleDateFormat性能优化 每一次时间格式化的时候都会去手动new SimpleDateFormat,每一次new都是一次较大的性能开销,因此想到的是用单例的形式来提高性能。由于SimpleDateFormat是线程不安全的,因此可以借助ThredLocal来实现线程安全。代码如下: 测试100万,500万,1000万循环不断的format时间测试性能: 100万的时候500万的时候100万的时候性能是有所提升的。............ 阅读详情

SimpleDateFormat 是非线程安全的

我们经常使用一些单例处理的实例作为工具类基础,然而SimpleDateFormat的单例实例在在并发情况下回出现各种灵异错误。
原因是因为SimpleDateFormat不是线程安全的。我们之所以忽视线程安全的问题,是因为从SimpleDateFormat提供的接口看不出来它是非线程安全的
只是在JDK文档的类注释有如下说明: Date formats are not synchronized.
推荐(建议)为每个线程创建独立的格式实例。如果多个线程同时访问一个格式,则它必须保持外部同步

 * </table>
 * </blockquote>
 *
 * <h4><a name="synchronization">Synchronization</a></h4>
 *
 * <p>
 * Date formats are not synchronized.
 * It is recommended to create separate format instances for each thread.
 * If multiple threads access a format concurrently, it must be synchronized
 * externally.
 *
 * @see          <a href="https://docs.oracle.com/javase/tutorial/i18n/format/simpleDateFormat.html">Java Tutorial</a>
 * @see          java.util.Calendar
 * @see          java.util.TimeZone
 * @see          DateFormat
 * @see          DateFormatSymbols
 * @author       Mark Davis, Chen-Lieh Huang, Alan Liu
 */
public class SimpleDateFormat extends DateFormat {

SimpleDateFormat继承了DateFormat,在DateFormat中定义了一个protected属性的 Calendar类的对象:calendar
Calendar类的的功能牵扯到时区与本地化等等,Jdk的实现中使用了成员变量来传递参数,这就造成在多线程的时候会出现错误。

// Called from Format after creating a FieldDelegate
    private StringBuffer format(Date date, StringBuffer toAppendTo,
                                FieldDelegate delegate) {
        // Convert input date to time field list
        calendar.setTime(date);

        boolean useDateFormatSymbols = useDateFormatSymbols();

        for (int i = 0; i < compiledPattern.length; ) {
            int tag = compiledPattern[i] >>> 8;
            int count = compiledPattern[i++] & 0xff;
            if (count == 255) {
                count = compiledPattern[i++] << 16;
                count |= compiledPattern[i++];
            }

            switch (tag) {
            case TAG_QUOTE_ASCII_CHAR:
                toAppendTo.append((char)count);
                break;

            case TAG_QUOTE_CHARS:
                toAppendTo.append(compiledPattern, i, count);
                i += count;
                break;

            default:
                subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
                break;
            }
        }
        return toAppendTo;
    }

线程不安全原因分析:

calendar.setTime(date)这条语句改变了calendar,稍后,calendar还会用到(在subFormat方法里),而这就是引发问题的根源。想象一下,在一个多线程环境下,有两个线程持有了同一个SimpleDateFormat的实例,分别调用format方法:
1. 线程1进来,调用format方法,改变了calendar
2. 中断
3. 线程2进来,开始执行,改变了calendar
4. 中断
5. 回到线程1,此时,calendar已然不是它所设的值,而是走上了线程2设计的道路。

  • 结论:如果多个线程同时争抢calendar对象,则会出现各种问题,时间不对,线程挂死等等。而SimpleDateFormat的实现用到成员变量calendar,唯一的好处,就是在调用subFormat时,少了一个参数,却带来了这许多的问题。

这里要提到的就是如何判断线程安全的方法标准了:那便是无状态,如果方法里面存在状态,并且可以被改变的情况的话,开发者就必须要考虑并发情况下的线程安全问题了

无状态方法的好处之一,就是它在各种环境下,都可以安全的调用。衡量一个方法是否是有状态的,就看它是否改动了其它的东西,比如全局变量,比如实例的字段。format方法在运行过程中改动了SimpleDateFormat的calendar字段,所以,它是有状态的。

开发过程中使用SimpleDateFormat的线程安全的使用方案

  1. 需要的时候创建新实例,对实例内存什么的不存在太多要求的小应用推荐使用
  2. 使用synchronized:同步SimpleDateFormat对象,缺点,高并发并发下存在性能问题
  3. 使用ThreadLocal将共享变量变为线程独享
  4. 抛弃JDK,使用其他类库中的时间格式化类:推荐Apache commons 里的FastDateForma 以及 Joda-Time类库

以下是测试代码:

package cn.coremail;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author linbingcheng
 * @version V1.0
 * @Title: UnsafeDateUtils
 * @Description: TODO
 * @email bclin@coremail.cn
 * @date 2018/6/26
 */
public class UnsafeDateUtils {

//    public static  String formatDate(Date date)throws ParseException {
//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        return sdf.format(date);
//    }
//
//    public static Date parse(String strDate) throws ParseException{
//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        return sdf.parse(strDate);
//    }

    private static final  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static  String formatDate(Date date)throws ParseException{
        return sdf.format(date);
    }

    public static Date parse(String strDate) throws ParseException{

        return sdf.parse(strDate);
    }
}

package cn.coremail;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author linbingcheng
 * @version V1.0
 * @Title: SafeDateUtils
 * @Description: TODO
 * @email bclin@coremail.cn
 * @date 2018/7/3
 */
public class SafeDateUtils  {

    /** 锁对象 */
    private static final Object lockObj = new Object();
    private static Map<String, ThreadLocal<SimpleDateFormat>> sdfMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();

    private static final String YYYYMMDDHHMMSS = "yyyy-MM-dd HH:mm:ss";

    private static SimpleDateFormat getSdf(final String pattern) {
        ThreadLocal<SimpleDateFormat> tl = sdfMap.get(pattern);
        // 此处的双重判断和同步是为了防止sdfMap这个单例被多次put重复的sdf
        if (tl == null) {
            synchronized (lockObj) {
                tl = sdfMap.get(pattern);
                if (tl == null) {
                    // 这里是关键,使用ThreadLocal<SimpleDateFormat>替代原来直接new SimpleDateFormat
                    tl = new ThreadLocal<SimpleDateFormat>() {
                        @Override
                        protected SimpleDateFormat initialValue() {
                            return new SimpleDateFormat(pattern);
                        }
                    };
                    sdfMap.put(pattern, tl);
                }
            }
        }

        return tl.get();
    }

    public static String formatDate(Date date, String pattern) throws ParseException {
        return getSdf(pattern).format(date);
    }

    public static Date parse(String strDate, String pattern) throws ParseException {
        return getSdf(pattern).parse(strDate);
    }
}


package cn.coremail;

import java.text.ParseException;
import java.util.Date;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * @author linbingcheng
 * @version V1.0
 * @Title: DateUtilTest
 * @Description: TODO
 * @email bclin@coremail.cn
 * @date 2018/6/26
 */
public class DateUtilTest {

    static CyclicBarrier cyclicBarrier =  new CyclicBarrier(5);

    public static class TestSimpleDateFormatThreadSafe extends Thread {
        @Override
        public void run() {
            System.out.println(this.getName() + ": is running");
            try {
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            while(true) {
                try {
//                    System.out.println(this.getName() + ":" + UnsafeDateUtils.parse("2018-06-24 06:02:20 "));
                    System.out.println(this.getName() + ":" + SafeDateUtils.parse("2018-06-24 06:02:20 ","yyyy-MM-dd HH:mm:ss"));
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                try {
                    this.join(1000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                yield();
            }
        }
    }


    public static void main(String[] args) {
        for(int i = 0; i < 5; i++){
            new TestSimpleDateFormatThreadSafe().start();
        }
    }
}
Java SimpleDateFormat线程安全与DateTimeFormatter迁移指南 SimpleDateFormat是Java中广泛使用的日期格式化工具,但其内部可变状态导致天然不支持多线程并发——这是理解Java时间API演进的关键起点。其核心问题源于非线程安全(thread-safe)设计与宽松解析机制,易引发时序竞争、数据错乱和隐式时区偏移。现代解决方案依托java.time包的不可变性(immutability)与领域模型,如DateTimeFormatter通过无状态设计彻底规避并发风险,并提供更精准的语义表达(如LocalDateTime、Instant)。该方案已成微服务、高 阅读详情

相关推荐

VxWorks 6.9虚拟机搭建指南:VMware上运行与Workbench调试完整教程

实时操作系统(RTOS)是嵌入式领域的关键技术,常用于工业控制、航空航天等对响应时间有严格要求的场景。在开发过程中,硬件资源有限往往制约调试效率,虚拟化技术为解决这一问题提供了新思路。VMware作为成熟的虚拟化平台,可以模拟x86硬件环境,为RTOS的验证与调试提供便利。然而,RTOS的引导方式与传统通用操作系统不同,其通过bootrom加载内核镜像,而非安装流程。理解这一原理后,在VMware中配置软驱、串口和网络,即可运行VxWorks 6.9,并结合Workbench进行在线调试。本文详细记录了从虚

weixin_34174105的博客 336

simpledateformat 线程安全问题及其优化方案

转载 simpledateformat非线程安全问题 ...

肥肥的博客 776

告别卡顿!手把手教你用北鲲云超算平台图形化提交COMSOL仿真任务(附RDP连接技巧)

本文详细介绍了如何利用北鲲云超算平台图形化提交COMSOL仿真任务,解决本地计算资源不足导致的卡顿问题。通过云端高性能计算,用户可轻松配置多核CPU和大内存资源,实现高效仿真计算,并附有RDP连接优化技巧,提升远程操作体验。

weixin_42519781的博客 380

高并发之——SimpleDateFormat类的线程安全问题和解决方案

首先问下大家:你使用的SimpleDateFormat类还安全吗?带着这个问题再来看本文。 提起SimpleDateFormat类,想必做过Java开发的童鞋都不会感到陌生。没错,它就是Java中提供的日期时间的转化类。这里,为什么说SimpleDateFormat类有线程安全问题呢?有些小伙伴可能会提出疑问:我们生产环境上一直在使用SimpleDateFormat类来解析和格式化日期和时间类型...

冰河的专栏 1万+

使用IntelliJ IDEA配置版本管理(SVN和Git)

在开发过程中,版本管理是必不可少的一部分,它可以帮助团队协作、管理代码历史记录,并且提供了代码回滚和分支管理等功能。本文将介绍如何使用IntelliJ IDEA配置版本管理,包括SVN和Git两种方式,另外列举了常见的问题并指导修复,以帮助开发者更高效地进行代码管理和协作。

2870

(论文速读)PTV3: 更简单、更快速、更强大的3D点云处理新突破

该论文提出PointTransformerV3(PTv3),通过简化设计实现3D点云处理的效率突破。该研究摒弃复杂注意力机制,转而采用点云序列化和补丁注意力等高效方法,将感受野从16点扩展到1024点,在保持高精度的同时实现3倍速度提升和10倍内存优化。PTv3在超过20个室内外场景任务中取得SOTA结果,ScanNet语义分割达79.4%mIoU,Waymo检测提升3.3%APH。核心创新包括四种空间填充曲线序列化模式和xCPE位置编码,为3D感知的工业应用提供了高效解决方案。

LJ1147517021的博客 1376

Java基础---SimpleDateFormat的线程安全

Java基础---SimpleDateFormat的线程安全

汤键的博客 1825

第1.2章 机器人SLAM计算机基础总结(二):VSCode教程及 AI等效率插件推荐

如何高效编写代码?怎样快速调试程序?**别担心,今天就给大家带来一款超强大的编程神器 ——VSCode,以及能让编程之路 “一马平川” 的实用插件推荐,无论你是新手小白还是资深大神,都能从中受益!

行知SLAM 1861

SimpleDateFormat线程安全问题

SimpleDateFormat线程安全问题简述SimpleDateFormat线程不安全问题示例format单线程下调用format多线程下调用parse单线程下调用parse多线程下调用线程不安全问题的原因SimpleDateFormat 与 DateFormatformat方法线程不安全原因parse方法线程不安全原因 简述SimpleDateFormat 在工作中,我们经常需要将日期在String和Date之间做转化,此时需要使用SimpleDateFormat类。 使用SimpleDateForm

qq_45464560的博客 1126

u-boot-2014.10移植第22天----添加nand flash命令支持(四)

解决问题:nand0: MTD Erase failure: -5 drivers/mtd/nand/nand_bbt.c文件中初始化bbt/* * Allocate memory (2bit per block) and clear the memory bad block * table. */ this->bbt = kzalloc(len, GFP_

fulinux的博客 3632

Test-Time Adaptation Induces Stronger Accuracy and Agreement-on-the-Line

TTA不管是否提高了OOD的泛化性都增强了线性的趋势,TTA的初始目的是为了增强OOD的表现,一个人可能会觉得TTA使得ACL更强的原因是他消除了ID和OOD之间的差距,因此所有模型间ID vs OOD的准确率接近于y=x这条线,但是实际上这个解释不能够反映我们的经验发现,因为虽然TTA将分布之间的偏移简化为一种放缩的分布,但是这个放缩的因子幅值可能很大,也就是α<<1但是r>>1,这就会导致很偏离y=x这条线,而且理论分析也是,虽然ID和OOD的差距很大,但是线性关系还在。

m0_62673638的博客 148

为什么SimpleDateFormat是线程不安全的

SimpleDateFormat 是线程不安全的,因为其内部状态会被多个线程共享并修改。解决方案有三种: 每个线程创建独立实例:简单且线程安全,但频繁创建实例会导致性能和内存开销较大,适合低并发场景。 使用 ThreadLocal:每个线程持有一个实例,避免频繁创建,提高性能,但需要注意 ThreadLocal 的生命周期管理,防止内存泄漏,适合高并发环境。 使用 java.time 包:DateTimeFormatter 等类是线程安全的且性能优越,推荐使用.

yiridancan的博客 1627

使用R语言读取和处理CSV数据集

查看数据集的结构str(data)# 查看数据集的摘要统计信息str()函数将显示数据集的结构信息,包括变量名称、类型和前几行的值。summary()函数将提供数据集的摘要统计信息,如均值、中位数、最小值、最大值等。

CyberGenius的博客 3856

还在使用SimpleDateFormat?

阅读本文大概需要 3.2 分钟。 前言 日常开发中,我们经常需要使用时间相关类,想必大家对SimpleDateFormat并不陌生。主要是用它进行时间的格式化输出和解析,挺方便快捷的,但是SimpleDateFormat并不是一个线程安全的类。在多线程情况下,会出现异常,想必有经验的小伙伴也遇到过。 下面我们就来分析分析SimpleDateFormat为什么不安全?是怎么引发的?以及多线程下...

良月柒 2470

GEE高阶应用——如何绘制2000-2022年土地利用变化轨迹时序图

土地利用变化是指在一定时间范围内,土地利用类型和结构发生的变化。时序变化是指这种变化随时间的推移而发生的序列变化。土地利用变化轨迹的时序变化具体介绍如下:首先,土地利用变化轨迹的时序变化体现在土地利用类型的演变上。在过去的几十年里,随着人口的增加、经济的发展和城市化的推进,土地利用类型发生了显著的变化。传统的农田逐渐减少,而城市、工业、交通、居住等用地的面积不断扩大。同时,随着环境保护意识的提高,生态保护用地、森林、湿地等绿色用地的面积也在增加。

此星光明博客 1113

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

【SimpleDateFormat】类线程不安全问题分析及解决方案

在日常开发中,我们经常需要去做日期格式转换,可能就会用到类。但是,如果使用不当,就很容易引发生产事故!如果是使用JDK 8+,则直接使用即可。如果使用的是低版本的JDK,则可以使用TheadLocal或解决方案。

Java全栈开发者 878

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

SimpleDateFormat并不是一个线程安全的类

一、前言 日常开发中,我们经常需要使用时间相关类,说到时间相关类,想必大家对SimpleDateFormat并不陌生。主要是用它进行时间的格式化输出和解析,挺方便快捷的,但是SimpleDateFormat并不是一个线程安全的类。在多线程情况下,会出现异常,想必有经验的小伙伴也遇到过。下面我们就来分析分析SimpleDateFormat为什么不安全?是怎么引发的?以及多线程下有那些SimpleD...

XT4625的专栏 956

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

关于SimpleDateFormat安全的时间格式化线程安全问题

想必大家对SimpleDateFormat并不陌生。SimpleDateFormat 是 Java 中一个非常常用的类,该类用来对日期字符串进行解析和格式化输出,但如果使用不小心会导致非常微妙和难以调试的问题,因为 DateFormat 和 SimpleDateFormat 类不都是线程安全的,在多线程环境下调用 format() 和 parse() 方法应该使用同步代码来避免问题。下面我们通过一

zxh87的专栏 7万+

java的SimpleDateFormat线程不安全出问题了,项目不支持java8,虚竹教你这一招

当系统只支持java8之前,使用 Apache 的FastDateFormat线程安全替换SimpleDateFormat线程不安全

小虚竹的专栏 3万+

java的SimpleDateFormat线程不安全出问题了,虚竹教你多种解决方案

try {由源码可知,最后是调用**parsedDate = calb.establish(calendar).getTime();**获取返回值。方法的参数是calendar,calendar可以被多个线程访问到,存在线程不安全问题。我们再来看看**calb.establish(calendar)**的源码,先清理值,再设值。但是这两个操作并不是原子性的,也没有线程安全机制来保证,导致多线程并发时,可能会引起cal的值出现问题了。验证SimpleDateFormat线程不安全。

2401_84544495的博客 565

Immutable设计 & SimpleDateFormat & DateTimeFormatter

value,offset和count这三个变量都是private的,并且没有提供setValue, setOffset和setCount等公共方法来修改这些值,所以在String类的外部无法修改String为了实现字符串池字符串池的实现可以在运行时节约很多heap空间,因为不同的字符串变量都指向池中的同一个字符串。但如果字符串是可变的,那么String intern将不能实现,因为这样的话,如果变量改变了它的值,那么其它指向这个值的变量的值也会一起改变为了安全 & 线程安全。

master-dragon的专栏 1168

SimpleDateFormat 线程安全问题的六种解决方案

综上所示:在解决解决 SimpleDateFormat 类的线程安全问题的几种方案中,局部变量法由于线程每次执行格式化时间时,都会创建 SimpleDateFormat 类的对象,这会导致创建大量的 SimpleDateFormat 对象,浪费运行空间和消耗服务器的性能,因为 JVM 创建和销毁对象是要耗费性能的。所以,不推荐在高并发要求的生产环境使用。synchronized 锁方式和 Lock 锁方式在处理问题的本质上是一致的,通过加锁的方式,使同一时刻只能有一个线程执行格式化日期和时间的操作。

爱琴孩的博客 507

SimpleDateFormat 的线程安全问题与解决方案

SimpleDateFormat 是一个以国别敏感的方式格式化和分析数据的具体类。 它允许格式化 (date -> text)、语法分析 (text -> date)和标准化。 但是 SimpleDateFormat 并不是一个线程安全的类,在多线程并发访问下会出现问题。通过以下代码进行检验,

张小琦的博客专栏 9552

【Java_基础深入】SimpleDateFormat.format()线程不安全的起因与解决方案

追踪问题 网上找到的最多讨论的就是 calendar的线程不安全操作传递到了SimpleDateFormat 针对Calendar进行断点观察,观察其值的变化 观察calendar的赋值链 SimpleDateFormat实例化过程1: calendar初始化的位置 追踪上层方法: 2.1 疑惑:无参构造调用了有参的构造方法,初始化了calendar 2.2 解惑:无参构造中使用了this...

chenghan_yang的博客 1329
上一篇: 基于maven进行编译打包的通用例子
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值