Java8新特性之线程安全日期类

2025-05-29 0 51

localdatetime

java8新特性之一,新增日期类

在项目开发过程中经常遇到时间处理,但是你真的用对了吗,理解阿里巴巴开发手册中禁用static修饰simpledateformat吗

通过阅读本篇文章你将了解到:

  • 为什么需要localdate、localtime、localdatetime【java8新提供的类】
  • java8新的时间api的使用方式,包括创建、格式化、解析、计算、修改

可以使用instant代替 date,localdatetime代替 calendar,datetimeformatter 代替 simpledateformat。

simpledateformat线程不安全

date如果不格式化,打印出的日期可读性差

?

1
tue sep 10 09:34:04 cst 2019

使用simpledateformat对时间进行格式化,但simpledateformat是线程不安全的 simpledateformat的format方法最终调用源码:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32
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类是里面基本都是final修饰的,calendar是共享变量,并且这个共享变量没有做线程安全控制。当多个线程同时使用相同的simpledateformat对象【如用static修饰的simpledateformat,一般会封装在工具类,复用】调用format方法时,多个线程会同时调用calendar.settime方法,可能一个线程刚设置好time值另外的一个线程马上把设置的time值给修改了导致返回的格式化时间可能是错误的。

在多并发情况下使用simpledateformat需格外注意:

simpledateformat除了format方法是线程不安全以外,parse方法也是线程不安全的。parse方法实际调用alb.establish(calendar).gettime()方法来解析,alb.establish(calendar)方法里主要完成了

  • 重置日期对象cal的属性值
  • 使用calb(calbuilder)中属性设置cal
  • 返回设置好的cal对象

但是这三步不是原子操作

simpledateformat如何保证线程安全

  • 避免线程之间共享一个simpledateformat对象,每个线程使用时都创建一次simpledateformat对象 => 创建和销毁对象的开销大
  • 对使用format和parse方法的地方进行加锁 => 线程阻塞性能差
  • 使用threadlocal保证每个线程最多只创建一次simpledateformat对象 => 较好的方法

date对时间处理比较麻烦,比如想获取某年、某月、某星期,以及n天以后的时间,如果用date来处理的话真是太难了,你可能会说date类不是有getyear、getmonth这些方法吗,获取年月日很easy,但都被弃用了啊

java8全新的日期和时间api

在使用java程序操作数据库时,我们需要把数据库类型与java类型映射起来。下表是数据库类型与java新旧api的映射关系:

数据库 对应java类(旧) 对应java类(新)
datetime java.util.date localdatetime
date java.sql.date localdate
time java.sql.time localtime
timestamp java.sql.timestamp localdatetime

localdate

只会获取年月日

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14
//获取当前年月日

localdate localdate = localdate.now();

//构造指定的年月日

localdate localdate1 = localdate.of(2019, 9, 10);

//获取年、月、日、星期几

int year = localdate.getyear();

int year1 = localdate.get(chronofield.year);

month month = localdate.getmonth();

int month1 = localdate.get(chronofield.month_of_year);

int day = localdate.getdayofmonth();

int day1 = localdate.get(chronofield.day_of_month);

dayofweek dayofweek = localdate.getdayofweek();

int dayofweek1 = localdate.get(chronofield.day_of_week);

localtime

只会获取几点几分几秒

?

1

2

3

4

5

6

7

8

9

10

11

12

13
//创建localtime

localtime localtime = localtime.of(13, 51, 10);

localtime localtime1 = localtime.now();

//获取小时

int hour = localtime.gethour();

int hour1 = localtime.get(chronofield.hour_of_day);

//获取分

int minute = localtime.getminute();

int minute1 = localtime.get(chronofield.minute_of_hour);

//获取秒

int second = localtime.getsecond();

int second1 = localtime.get(chronofield.second_of_minute);

localdatetime

获取年月日时分秒,等于localdate+localtime

?

1

2

3

4

5

6

7

8

9

10

11

12
//创建对象

localdatetime localdatetime = localdatetime.now();

localdatetime localdatetime1 = localdatetime.of(2019, month.september, 10, 14, 46, 56);

//localdate+localtime-->localdatetime

localdatetime localdatetime2 = localdatetime.of(localdate, localtime);

localdatetime localdatetime3 = localdate.attime(localtime);

localdatetime localdatetime4 = localtime.atdate(localdate);

//获取localdate

localdate localdate2 = localdatetime.tolocaldate();

//获取localtime

localtime localtime2 = localdatetime.tolocaltime();

zoneddatetime

localdatetime总是表示本地日期和时间,要表示一个带时区的日期和时间,我们就需要zoneddatetime

可以简单地把zoneddatetime理解成localdatetimezoneidzoneidjava.time引入的新的时区类,注意和旧的java.util.timezone区别。

创建一个zoneddatetime对象

?

1

2

3

4
// 默认时区

zoneddatetime zbj = zoneddatetime.now();

// 用指定时区获取当前时间

zoneddatetime zny = zoneddatetime.now(zoneid.of("america/new_york"));

结果:

2019-09-15t20:58:18.786182+08:00[asia/shanghai]
2019-09-15t08:58:18.788860-04:00[america/new_york]

另一种创建方式是通过给一个localdatetime附加一个zoneid,就可以变成zoneddatetime

?

1

2

3
localdatetime ldt = localdatetime.of(2019, 9, 15, 15, 16, 17);

zoneddatetime zbj = ldt.atzone(zoneid.systemdefault());

zoneddatetime zny = ldt.atzone(zoneid.of("america/new_york"));

时区转换

要转换时区,首先我们需要有一个zoneddatetime对象,然后,通过withzonesameinstant()将关联时区转换到另一个时区,转换后日期和时间都会相应调整。

?

1

2

3
zoneddatetime zbj = zoneddatetime.now(zoneid.of("asia/shanghai"));

// 转换为纽约时间:

zoneddatetime zny = zbj.withzonesameinstant(zoneid.of("america/new_york"));

zoneddatetime仍然提供了plusdays()等加减操作。

要特别注意,时区转换的时候,由于夏令时的存在,不同的日期转换的结果很可能是不同的。这是北京时间9月15日的转换结果:

2019-09-15t21:05:50.187697+08:00[asia/shanghai]
2019-09-15t09:05:50.187697-04:00[america/new_york]

这是北京时间11月15日的转换结果:

2019-11-15t21:05:50.187697+08:00[asia/shanghai]
2019-11-15t08:05:50.187697-05:00[america/new_york]

两次转换后的纽约时间有1小时的夏令时时差。涉及到时区时,千万不要自己计算时差,否则难以正确处理夏令时。有了zoneddatetime,将其转换为本地时间就非常简单:

?

1

2
zoneddatetime zdt = ...

localdatetime ldt = zdt.tolocaldatetime();

转换为localdatetime时,直接丢弃了时区信息。

instant

获取秒数或时间戳

?

1

2

3

4

5

6

7
//创建instant对象

instant instant = instant.now();

//获取秒数

long currentsecond = instant.getepochsecond();

//获取毫秒数

long currentmilli = instant.toepochmilli();

long l = system.currenttimemillis();

system.currenttimemillis()也可以获取毫秒数。

日期计算

localdate、localtime、localdatetime、instant为不可变对象,修改这些对象对象会返回一个副本

增加、减少年数、月数、天数等,以localdatetime为例

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16
//修改localdate、localtime、localdatetime、instant

localdatetime localdatetime = localdatetime.of(2019, month.september, 10,

14, 46, 56);

//增加一年

localdatetime = localdatetime.plusyears(1);

localdatetime = localdatetime.plus(1, chronounit.years);

//减少一个月

localdatetime = localdatetime.minusmonths(1);

localdatetime = localdatetime.minus(1, chronounit.months);

//通过with修改某些值

//修改年为2020

localdatetime = localdatetime.withyear(2020);

//修改为2022

localdatetime = localdatetime.with(chronofield.year, 2022);

//还可以修改月、日

有些时候想知道这个月的最后一天是几号、下个周末是几号,通过提供的时间和日期api可以很快得到答案

?

1

2
localdate localdate = localdate.now();

localdate localdate1 = localdate.with(temporaladjusters.firstdayofyear());

比如通过firstdayofyear()返回了当前年的第一天日期,还有很多方法这里不在举例说明

格式化时间

datetimeformatter默认提供了多种格式化方式,如果默认提供的不能满足要求,可以通过datetimeformatter的ofpattern方法创建自定义格式化方式

?

1

2

3

4

5

6
localdate localdate = localdate.of(2019, 9, 10);

string s1 = localdate.format(datetimeformatter.basic_iso_date);

string s2 = localdate.format(datetimeformatter.iso_local_date);

//自定义格式化

datetimeformatter datetimeformatter = datetimeformatter.ofpattern("dd/mm/yyyy");

string s3 = localdate.format(datetimeformatter);

解析时间

和simpledateformat相比,datetimeformatter是线程安全

?

1

2
localdate localdate1 = localdate.parse("20190910", datetimeformatter.basic_iso_date);

localdate localdate2 = localdate.parse("2019-09-10", datetimeformatter.iso_local_date);

datetimeformatter替代simpledateformat

使用旧的date对象时,我们用simpledateformat进行格式化显示。使用新的localdatetimezonedlocaldatetime时,我们要进行格式化显示,就要使用datetimeformatter

simpledateformat不同的是,datetimeformatter不但是不变对象,它还是线程安全的。因为simpledateformat不是线程安全的,使用的时候,只能在方法内部创建新的局部变量。而datetimeformatter可以只创建一个实例,到处引用。

?

1

2

3

4

5

6
//构造器1:传入格式字符串

datetimeformatter formatter = datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss");

//构造器2:传入格式字符串和地区

datetimeformatter formatter = datetimeformatter.ofpattern("e, yyyy-mmmm-dd hh:mm:ss", locale.us);

datetimeformatter formatter = datetimeformatter.ofpattern("e, yyyy-mmmm-dd hh:mm:ss", locale.china);

datetimeformatter底层原理

datetimeformatter线程安全的?为什么?

源码:

Java8新特性之线程安全日期类

很明显,通过final修饰类,不可被继承,final修饰变量,做成了不可变类,类似string,不仅线程安全而且高效。全局可以只有一个对象,多个线程引用。

format和parse线程安全替代

使用localdatetime的format和parse方法,传入对应的datetimeformatter对象参数,实际也是调用datetimeformatter的format和parse方法,实现日期格式化和解析,是线程安全的。

datetimeformatter类解析localdatetime中的日期变量,转成stringbuilder返回。localdatetime等新出的日期类全是final修饰的类,不能被继承,且对应的日期变量都是final修饰的,也就是不可变类。赋值一次后就不可变,不存在多线程数据问题。

Java8新特性之线程安全日期类

到此这篇关于java8新特性之线程安全日期类的文章就介绍到这了,更多相关java8线程安全日期类内容请搜索快网idc以前的文章或继续浏览下面的相关文章希望大家以后多多支持快网idc!

原文链接:https://blog.csdn.net/qq_43409401/article/details/115696152

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 Java8新特性之线程安全日期类 https://www.kuaiidc.com/108104.html

相关文章

发表评论
暂无评论