首页 > 编程知识 正文

dateformat类(new date)

时间:2023-05-05 07:05:18 阅读:76101 作者:717

介绍简单数据格式是JDK提供的rt包下的时间格式转换的类,类名为java.text.SimpleDateFormat。

从这个班的评论中可以看出:

1、可以将date转换为文本格式

2、可以将文本转换为date格式

3、可以针对不同的时区进行转换

4、允许你采用定制的时间patter。 可以使用applyPattern方法更改转换格式

他自己提供了部分字母含义,但留下了’a’to’z’和’a’to’z’所有未定义的文字,已经提供如下。

这里需要注意的是大小写的区别。 例如,y和y,m和m。 有各种各样的值范围,如h和k、k和h。

常见使用方法:

publicclasssimpledateformattest { publicstaticvoidmain [ ] args } { try { simpledateformatsdf _ text2date=newsimpling datedate System.out.println ('转换后的时间date:' date ); simpledateformatsdf _ date2 text=newsimpledateformat (' yyyy _ mm _ ddhh-mm-ss ' ); string datestring=SDF _ date2 text.format (date; System.out.println ('转换后的时间String:' dateString ); }catch(parseexceptione ) { System.out.println )日期转换错误: ' e ); }}输出结果:

转换后的时间date : wedmar 0309336049336012 CST 2021

转换后的时间String:2021_03_03 09-49-12

常见日期和字符串之间的格式转换可以通过简单日期格式提供的parse、format方式实现。

言归正传,为什么他们是非线程且安全呢?

在官方文件中,关于是否同时安全的一栏中说:

同步化

dateformatsarenotsynchronized.itisrecommendedtocreateseparateformatinstancesforeachthread.ifmultiplethreadsaccessaformatcococod

正如第一句中所说,时间格式转换是非线程且安全的。 (第一种情况是为每个线程创建SimpleDateFormat对象(官方推荐)。 (第二种情况)如果在并发环境中使用简单数据格式对象,则需要同步串行。

脑洞大:

文档中说Date formats是非线程安全的,但是parse方法是线程安全的吗?

一起看看吧!

源代码的解密首先从格式开始。 示例代码:

simpledateformatsdf _ date2 text=newsimpledateformat (' yyyy _ mm _ ddhh-mm-ss ' ); string datestring=SDF _ date2 text.format (date; publicfinalstringformat (datedate ) return format (date,new StringBuffer ),DontCareFieldPosition.INSTANCE ).tostrince

示例代码使用第一个,无论是第一个还是第三个,最终调用第二个。 第三,可以输入Number类型,即时间戳。

调用的format是一种抽象的方法,提供您自定义的实现:

publicabstractstringbufferformat (date,StringBuffer toAppendTo,字段位置字段位置); 让我们看看在简单日期格式中的实现。

@Override publi

c StringBuffer format(Date date, StringBuffer toAppendTo,FieldPosition pos){ // 指定解析的索引位置 pos.beginIndex = pos.endIndex = 0; return format(date, toAppendTo, pos.getFieldDelegate()); } // Called from Format after creating a FieldDelegate private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) { // 并发问题出现的关键,对全局成员变量calendar设置为date时间,后续时间取值从calendar中取 calendar.setTime(date); for (int i = 0; i < compiledPattern.length; ) { // 对时间模板进行解析 ... 具体细节暂时忽略 } return toAppendTo; }

我们主要关注一下上面代码中的:

// 并发问题出现的关键,对全局成员变量calendar设置为date时间,后续时间取值从calendar中取calendar.setTime(date);

其中calendar是对象的成员变量,

/** * The {@link Calendar} instance used for calculating the date-time fields * and the instant of time. This field is used for both formatting and * parsing. */ protected Calendar calendar;

查看注释,我们发现,他说这个字段会用来formatting and parsing,那么估计在parse的时候也是有同样问题的了。

趁热打铁,我们看下parse代码:

SimpleDateFormat sdf_text2date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = sdf_text2date.parse("2021-03-03 09:49:12"); public Date parse(String source) throws ParseException { ParsePosition pos = new ParsePosition(0); Date result = parse(source, pos);// ...省略无用细节 return result; }

也是一个抽象方法,供我们拓展:

public abstract Date parse(String source, ParsePosition pos);

我们查看SimpleDateFormat的实现

@Override public Date parse(String text, ParsePosition pos) { int start = pos.index; int oldStart = start; int textLength = text.length(); boolean[] ambiguousYear = {false}; CalendarBuilder calb = new CalendarBuilder(); for (int i = 0; i < compiledPattern.length; ) { //....解析过程,省略,主要是解析出不同时间域的数值到CalendarBuilder 对象中 } } Date parsedDate; // 关键点,将CalendarBuilder中解析出的时间域设置发布到,成员变量calendar中 parsedDate = calb.establish(calendar).getTime(); return parsedDate; }

可见,在parse方法中,也是会将最终解析的时间设置到成员变量calendar中,仍然存在并发问题。

并发问题分析

看了上面的format与parse方法,我们分析一下这个并发问题,因为calendar是SimpleDateFormat类的一个普通成员变量,那么一个SimpleDateFormat对象就对应一个calendar对象,当在多线程环境下,如当前有线程A,B同时在format操作,A线程在设置完date之后开始解析:

calendar.setTime(date);

在A线程解析到一半时,B线程也统一使用该SimpleDateFormat对象,将calendar的值设置成另一个时间,那么此时A线程解析时读取的剩余的时间就是B线程设置的值了。parse方法也是同理。

同时,根据代码发现,parse与format方法,是共用一个calendar成员变量的,所以如果多线程共用SimpleDateFormat对象,即使A线程做format操作,B线程做parse操作,也会存在并发问题。

错误代码展示

为了方便你更快复现这个问题,我写了一个测试:

@Test public void testSimpleDateFormatUnSynchronized() { DateFormat sdf_date2text = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date1 = new Date(1614820308016L); Date date2 = new Date(1615820309016L); /** 输出 * 初始定义时间1: 2021-03-04 09:11:48 * 初始定义时间2: 2021-03-15 22:58:29 */ System.out.println("初始定义时间1: " + sdf_date2text.format(date1)); System.out.println("初始定义时间2: " + sdf_date2text.format(date2)); Thread t1 = new Thread(() -> { int i = 0; while (i<100) { String dateString = sdf_date2text.format(date1); if (!dateString.equals("2021-03-04 09:11:48")) { System.out.println(Thread.currentThread().getName() + "异常时间: " + dateString); } i++; } },"线程1"); Thread t2 = new Thread(() -> { int i = 0; while (i<100) { String dateString = sdf_date2text.format(date2); if (!dateString.equals("2021-03-15 22:58:29")) { System.out.println(Thread.currentThread().getName() + "异常时间: " + dateString); } i++; } },"线程2"); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } }

初始定义时间1: 2021-03-04 09:11:48
初始定义时间2: 2021-03-15 22:58:29
线程1异常时间: 2021-03-15 22:58:29
线程1异常时间: 2021-03-04 22:58:29
线程2异常时间: 2021-03-04 22:58:29
线程1异常时间: 2021-03-04 22:58:29
线程2异常时间: 2021-03-04 09:11:48
线程1异常时间: 2021-03-15 22:58:29
线程1异常时间: 2021-03-15 22:58:29
线程1异常时间: 2021-03-15 22:58:29

我们可以看到,在非线程安全情况下并发访问SimpleDateFormat对象,出现的结果就是混乱的,甚至线程1得到的是线程2的时间,这样整个业务系统就出bug了。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。