SimpleDateFormat非线程安全问题

news2024/11/29 2:50:25

文章目录

    • 1. SimpleDateFormat介绍
    • 2. 测试SimpleDateFormat的非线程安全性
    • 3. 解决方案一
    • 4. 解决方案二

1. SimpleDateFormat介绍

SimpleDateFormat是Java中的一个类,用于将日期对象格式化为特定的字符串表示形式,或者将特定格式的字符串解析为日期对象。它是java.text包中的一部分,提供了许多不同的模式(pattern)来定义日期和时间的格式。以下是SimpleDateFormat的使用详细介绍:

  • 创建SimpleDateFormatd对象
SimpleDateFormat sdf = new SimpleDateFormat(pattern);

其中,pattern是一个字符串,定义了日期和时间的格式。可以使用不同的模式字符来构建自定义的格式。例如,"yyyy-MM-dd"表示年份-月份-日期的格式。

  • 格式化日期对象为字符串
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = new Date();
String formattedDate = sdf.format(date);
System.out.println(formattedDate);
  • 解析字符串为日期对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateString = "2023-05-20";
Date date = sdf.parse(dateString);
System.out.println(date);
  • 模式字符

SimpleDateFormat使用一些特定的模式字符来定义日期和时间的格式。以下是一些常用的模式字符:

y: 年份(例如:2023)
M: 月份(例如:05)
d: 日期(例如:20)
H: 小时(24小时制,例如:13)
h: 小时(12小时制,例如:01)
m: 分钟(例如:30)
s: 秒(例如:45)
S: 毫秒(例如:123)
可以根据需要组合这些模式字符来构建自定义的日期和时间格式。

  • 完整使用案例
import java.text.SimpleDateFormat;
import java.util.Date;

public class SimpleDateFormatExample {
    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        
        // 格式化日期对象为字符串
        Date date = new Date();
        String formattedDate = sdf.format(date);
        System.out.println("Formatted Date: " + formattedDate);
        
        // 解析字符串为日期对象
        String dateString = "2023-05-20";
        try {
            Date parsedDate = sdf.parse(dateString);
            System.out.println("Parsed Date: " + parsedDate);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述
上面介绍完了SimpleDateFormat的基本使用方法,但它在多线程环境中使用容易造成数据转换及处理不准确,因为SimpleDateFormat并不是线程安全的。

2. 测试SimpleDateFormat的非线程安全性

public class Main{
    public static void main(String[] args) {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
        String[] dateString=new String[]{
                "2000-01-01", "2000-01-02", "2000-01-03", "2000-01-04", "2000-01-05",
                "2000-01-06", "2000-01-07", "2000-01-08", "2000-01-09", "2000-01-10"
        };
        MyThread[] threads=new MyThread[10];
        for (int i = 0; i < 10; i++) {
            threads[i]=new MyThread(sdf,dateString[i]);
        }
        for (int i = 0; i < 10; i++) {
            threads[i].start();
        }
    }
}
class  MyThread extends Thread{
   private  SimpleDateFormat simpleDateFormat;
   private  String dateString;
   public  MyThread(SimpleDateFormat simpleDateFormat,String dateString)
   {
       super();
       this.simpleDateFormat=simpleDateFormat;
       this.dateString=dateString;
   }
   @Override
    public void run(){
       try {
           //将字符床解析为date格式,然后有格式化为String格式
           Date dateRef=simpleDateFormat.parse(dateString);
           String newDateString=simpleDateFormat.format(dateRef).toString();
           if(!newDateString.equals(dateString))
           {
               System.out.println("ThreadName="+this.getName()+"错误,日期字符串为"+dateString+"转换成的日期"+newDateString);
           }
       }catch (ParseException e)
       {
           e.printStackTrace();
       }
   }
}

在这里插入图片描述

impleDateFormat是线程不安全的主要原因是它内部维护了一个Calendar对象来进行日期和时间的解析和格式化操作。由于Calendar对象本身是可变的,多个线程同时调用SimpleDateFormat的方法可能会导致竞争条件和数据不一致的问题。具体来说,SimpleDateFormat的parse()和format()方法在解析和格式化日期时会使用Calendar对象来进行计算和存储中间结果。然而,Calendar并不是一个线程安全的类。在多线程环境下,多个线程同时调用SimpleDateFormat的方法可能会共享同一个Calendar对象,导致并发访问的问题。例如,如果一个线程在解析日期时正在修改Calendar对象的状态,而另一个线程同时调用format()方法,那么就有可能出现数据不一致的情况。一个线程可能会看到另一个线程修改过的Calendar状态,导致解析或格式化结果出错。根据问题出现的原因,我们可以用下面方案进行解决:

3. 解决方案一

public class Main{
    public static void main(String[] args) {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
        String[] dateString=new String[]{
                "2000-01-01", "2000-01-02", "2000-01-03", "2000-01-04", "2000-01-05",
                "2000-01-06", "2000-01-07", "2000-01-08", "2000-01-09", "2000-01-10"
        };
        MyThread[] threads=new MyThread[10];
        for (int i = 0; i < 10; i++) {
            threads[i]=new MyThread(sdf,dateString[i]);
        }
        for (int i = 0; i < 10; i++) {
            threads[i].start();
        }

    }
}
class  MyThread extends Thread{
   private  SimpleDateFormat simpleDateFormat;
   private  String dateString;
   public  MyThread(SimpleDateFormat simpleDateFormat,String dateString)
   {
       super();
       this.simpleDateFormat=simpleDateFormat;
       this.dateString=dateString;
   }
   @Override
    public void run(){
       try {
           //使用DateTools类可以每次都创建一个新的SimpleDateFormat
              Date dateRef=DateTools.parse("yyyy-MM-dd",dateString);
              String newDateString=DateTools.format("yyyy-MM-dd",dateRef).toString();
           if(!newDateString.equals(dateString)){
               System.out.println("ThreadName="+this.getName()+"报错了,日期字符串"+dateString+"转换后的日期为"+newDateString);
           }

       }catch (ParseException e)
       {
           e.printStackTrace();
       }
   }
}
class  DateTools{
    public static Date parse(String formatPattern,String dateString) throws ParseException {
        return new SimpleDateFormat(formatPattern).parse(dateString);
    }
    public static String format(String formatPattern ,Date date){
        return  new SimpleDateFormat(formatPattern).format(date).toString();
    }
}

运行代码可以发现控制台什么都没输出,说明没有出现日期格式转换错误,该解决方法的原理就是创建了多个SimpleDateFormat类

4. 解决方案二

public class Main{
    public static void main(String[] args) {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
        String[] dateString=new String[]{
                "2000-01-01", "2000-01-02", "2000-01-03", "2000-01-04", "2000-01-05",
                "2000-01-06", "2000-01-07", "2000-01-08", "2000-01-09", "2000-01-10"
        };
        MyThread[] threads=new MyThread[10];
        for (int i = 0; i < 10; i++) {
            threads[i]=new MyThread(sdf,dateString[i]);
        }
        for (int i = 0; i < 10; i++) {
            threads[i].start();
        }

    }
}
class  MyThread extends Thread{
   private  SimpleDateFormat simpleDateFormat;
   private  String dateString;
   public  MyThread(SimpleDateFormat simpleDateFormat,String dateString)
   {
       super();
       this.simpleDateFormat=simpleDateFormat;
       this.dateString=dateString;
   }
   @Override
    public void run(){
       try {
           //使用DateTools类可以每次都创建一个新的SimpleDateFormat
              Date dateRef=DateTools.getSimpleDateFormat("yyyy-MM-dd").parse(dateString);
              String newDateString=DateTools.getSimpleDateFormat("yyyy-MM-dd").format(dateRef).toString();
           if(!newDateString.equals(dateString)){
               System.out.println("ThreadName="+this.getName()+"报错了,日期字符串"+dateString+"转换后的日期为"+newDateString);
           }

       }catch (ParseException e)
       {
           e.printStackTrace();
       }
   }
}
class  DateTools{
      private static ThreadLocal<SimpleDateFormat> t1=new ThreadLocal<SimpleDateFormat>();
      public static SimpleDateFormat getSimpleDateFormat(String dateParttern){
          SimpleDateFormat sdf=null;
          sdf=t1.get();
          if(sdf==null){
              sdf=new SimpleDateFormat(dateParttern);
              t1.set(sdf);
          }
          return sdf;
      }
}

同样也成功的解决了SimpleDateFormat线程问题,这里使用的是ThreadLocal,它可以使线程绑定到指定的对象,使用该类可以解决多线程环境中类SimpleDateFormat处理日期时出现错误的问题

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

netstat 连接通信的信息和状态、以及ss

netstat 常用参数 t 只显示tcpu只显示udpnnum 数字形式显示地址和端口号l listen 显示监听端口 pprogram 显示进程aall 所有连接和监听r显示路由表 netstat -lnp 显示服务监听端口tcpudpsocket &#xff0c;socket 文件也用来同一台服务器的进程之间通信的…

Spring中的MergedBeanDefinitionPostProcessor有什么作用 ?

Spring中的MergedBeanDefinitionPostProcessor有什么作用 ? 引言调用时机加载bean定义的几种方式postProcessMergedBeanDefinition接口作用小结 引言 MergedBeanDefinitionPostProcessor这个Bean后置处理器大家可能关注的比较少,其本身也只提供了一个bean生命周期回调接口: …

iptable 防火墙一

目录 iptables概述netfilter/iptables 关系四表五链四表&#xff1a;五链&#xff1a; 规则链之间的匹配顺序主机型防火墙&#xff1a;规则链内的匹配顺序&#xff1a; iptables 安装iptables防火墙的配置方法&#xff1a;iptables 命令行配置方法&#xff1a;常用的控制类型&a…

一文读懂大语言模型

以ChatGPT为代表的大语言模型被很多人认为是新一轮科技革命的起点&#xff0c;本文旨在通过概念性介绍&#xff0c;让普通人能够尽可能理解人工智能以及大语言模型的基本概念&#xff0c;从而了解这些技术能做以及不能做什么。原文: A Very Gentle Introduction to Large Langu…

v4l2数据结构分析

v4l2数据结构分析 文章目录 v4l2数据结构分析Video4Linux2设备v4l2_device媒体设备media_deviceVideo4Linux2子设备v4l2_subdevVideo4Linux2子设备的操作集v4l2_subdev_opsVideo4Linux2子设备的内部操作集v4l2_subdev_internal_opsVideo4Linux2控制处理器v4l2_ctrl_handlerVide…

微信自动聊天机器狗,配置chatGPT,比Siri还智能!

大家好&#xff0c;我是TheWeiJun&#xff1b;最近看见微信里各个群聊都在聊chatGPT&#xff0c;甚至有的大佬们都把chatGPT接入了微信群聊&#xff0c;于是就有粉丝来找小编&#xff0c;希望能出一期chatGPT的文章&#xff1b;故今天这篇文章我将手把手教大家如何实现并自定义…

学习《信息系统项目管理师教程》第4版应关注的PMBOK的巨大变化

学习《信息系统项目管理师教程》第4版应关注的PMBOK的巨大变化 《信息系统项目管理师教程》的第4版比起第3版来有不少变化。但是&#xff0c;这种变化完全没有体现出PMBOK第7版带来的巨大变化。 因为&#xff0c;在从《信息系统项目管理师教程》第3版出版的2017年到现在&…

uvc驱动中的v4l2

uvc驱动中的v4l2 文章目录 uvc驱动中的v4l2v4l2_device_registervideo_register_devicev4l2_ioctlsvideo_usercopy v4l2_device_register /driver/media/v4l2-core/v4l2-device.c uvc_probe->v4l2_device_register v4l2_device_register 只是用于初始化一些东西&#xff0c…

【数项级数】无穷个数相加一定是个数吗?

数项级数 引入思考问题转化 定义总结重要的例子练习题 引入 思考 数项级数&#xff0c;其实就是要解决无穷个数相加的问题。 而对于无穷求和的问题&#xff0c;思考&#xff1a;无穷个数相加一定是个数吗&#xff1f; 下面&#xff0c;我们来举几个例子&#xff1a; 1 2 2 …

创世纪:比特币诞生记

比特币的诞生 1. 创始区块2. 第一个举手的人3. 比特币的疯狂 1. 创始区块 2008年10月31日纽约时间下午2点10分&#xff0c;自称中本聪的人向一个邮件列表&#xff0c;包括密码学专家和爱好者几百个成员&#xff0c;发送了一封电子邮件。“我一直在研究一个新的电子现金系统&am…

springboot旅游资源管理系统门票酒店预订系统_b0a6b

Spring Boot 是 Spring 家族中的一个全新的框架&#xff0c;它用来简化Spring应用程序的创建和开发过程。也可以说 Spring Boot 能简化我们之前采用SSM&#xff08;Spring MVC Spring MyBatis &#xff09;框架进行开发的过程。config&#xff1a;主要用来存储配置文件&#…

chatgpt赋能Python-pythoncontinue怎么用

Python continue语句&#xff1a;提高代码效率的绝佳工具 什么是Python continue语句&#xff1f; Python的continue语句可以使循环跳过当前的迭代。这意味着如果在循环内部存在满足某特定条件的语句&#xff0c;那么我们就可以使用continue语句跳过当前循环。Python中的cont…

【Nodejs】使用Nodejs搭建HTTP服务,并实现公网远程访问

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 转载自内网穿透工具的文章&#xff1a;使用Nodejs搭建HTTP服务&#xff0c;并实现公网远程访问「内网穿透」 前言 Node.js…

Mybatis Plus实现乐观锁

文章目录 1 概念2 实现思路3 实现步骤步骤1:数据库表添加列步骤2:在模型类中添加对应的属性步骤3:添加乐观锁的拦截器步骤4:执行更新操作 1 概念 在讲解乐观锁之前&#xff0c;我们还是先来分析下问题: 业务并发现象带来的问题 : 秒杀 假如有100个商品或者票在出售&#xff…

【数据分享】我国各县1992—2019年社会经济指标(7个指标\无需转发)

社会经济指标常用于各项研究中&#xff0c;之前基于《中国城市统计年鉴》我们整理了1999-2020年地级市的地区生产总值及一二三产构成数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff01; 我们发现在学者Chen Yahan在Figshare 数据库中提供了1992—2019年以县为…

动态规划-状态压缩DP

[SCOI2005] 互不侵犯 题目描述 https://www.luogu.com.cn/problem/P1896 在NN的棋盘里面放K个国王&#xff0c;使他们互不攻击&#xff0c;共有多少种摆放方案。国王能攻击到它上下左右&#xff0c;以及左上左下右上右下八个方向上附近的各一个格子&#xff0c;共8个格子。 …

ADS-B接收机Radarcape

1.设备简介 Radarcape是一款便携、高性能、功能强大的ADS-B地面接收机。Radarcape的设备清单包含&#xff1a;ADS-B接收机主机&#xff0c;专业级ADS-B天线&#xff0c;GPS天线&#xff0c;电源线&#xff0c;网线。 2. 功能特点 Radarcape可以通过网口输出飞机的原始数据D…

开源字节 CRM 系统

开源字节CRM是一款SaaS模式的客户关系管理软件&#xff0c;基于钉钉平台进行研发&#xff0c;以客户管理为核心&#xff0c;包含客户管理、销售全流程管理&#xff0c;合同订单、工单管理、移动审批、数据分析六大模块。 旨在助力企业销售全流程精细化、数字化管理&#xff0c…

Godot引擎 4.0 文档 - 入门介绍 - Godot简介

本文为Google Translate英译中结果&#xff0c;DrGraph在此基础上加了一些校正。英文原版页面&#xff1a;Introduction to Godsot — Godot Engine (stable) documentation in English Godot简介 本文旨在帮助您确定 Godot 是否适合您。我们将介绍该引擎的一些广泛功能&#…

Linux中文件描述符fd和文件指针filp的理解

简单归纳&#xff1a;fd只是一个整数&#xff0c;在open时产生。起到一个索引的作用&#xff0c;进程通过PCB中的文件描述符表找到该fd所指向的文件指针filp。 文件描述符的操作(如: open)返回的是一个文件描述符,内核会在每个进程空间中维护一个文件描述符表, 所有打开的文件…