返回顶部
首页 > 资讯 > 后端开发 > Python >Java8新特性之线程安全日期类
  • 630
分享到

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

2024-04-02 19:04:59 630人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录LocalDateTimeSimpleDateFORMat线程不安全SimpleDateFormat如何保证线程安全Java8全新的日期和时间apiLocalDateLocalT

LocalDateTime

Java8新特性之一,新增日期类。

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

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

  • 为什么需要LocalDate、LocalTime、LocalDateTime【java8新提供的类】
  • Java8新的时间API的使用方式,包括创建、格式化、解析、计算、修改

可以使用Instant代替 Date,LocalDateTime代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat。

SimpleDateFormat线程不安全

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


Tue Sep 10 09:34:04 CST 2019

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


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

只会获取年月日


//获取当前年月日
        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

只会获取几点几分几秒


//创建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


//创建对象
        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对象


// 默认时区
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


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()将关联时区转换到另一个时区,转换后日期和时间都会相应调整。


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,将其转换为本地时间就非常简单:


ZonedDateTime zdt = ...
LocalDateTime ldt = zdt.toLocalDateTime();

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

Instant

获取秒数或时间戳


//创建Instant对象
        Instant instant = Instant.now();
        //获取秒数
        long currentSecond = instant.getEpochSecond();
        //获取毫秒数
        long currentMilli = instant.toEpochMilli();
        long l = System.currentTimeMillis();

System.currentTimeMillis()也可以获取毫秒数。

日期计算

LocalDate、LocalTime、LocalDateTime、Instant为不可变对象,修改这些对象对象会返回一个副本

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


//修改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可以很快得到答案


LocalDate localDate = LocalDate.now();
LocalDate localDate1 = localDate.with(TemporalAdjusters.firstDayOfYear());

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

格式化时间

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


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是线程安全的


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:传入格式字符串
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线程安全的?为什么?

源码:

在这里插入图片描述

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

format和parse线程安全替代

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

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

在这里插入图片描述

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

--结束END--

本文标题: Java8新特性之线程安全日期类

本文链接: https://lsjlt.com/news/123823.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Java8新特性之线程安全日期类
    目录LocalDateTimeSimpleDateFormat线程不安全SimpleDateFormat如何保证线程安全Java8全新的日期和时间APILocalDateLocalT...
    99+
    2024-04-02
  • java8新特性之日期时间API
    目录jdk8之前 一、java.lang.System二、java.util.Date And java.sql.Date三、java.text.SimpleDateFor...
    99+
    2024-04-02
  • 怎么在Java8中实现一个线程安全日期类
    这期内容当中小编将会给大家带来有关怎么在Java8中实现一个线程安全日期类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。LocalDateTimeJava8新特性之一,新增日期类。在项目开发过程中经常遇到...
    99+
    2023-06-14
  • Java8新特性Optional类及新时间日期API示例详解
    目录Optional类以前对null的处理Optional类介绍Optional的基本使用Optional的常用方法新时间日期API旧版日期时间的问题新日期时间API介绍日期时间的常...
    99+
    2022-11-13
    Java8 Optional类时间日期API Java8 新特性
  • 深入理解Java8新特性之新日期时间API的应用
    目录1.新旧对比(线程安全问题)2.LocalDate3.LocalTime4.LocalDateTime5.Instant6.Duration、Period7.TestTempor...
    99+
    2024-04-02
  • 熟练掌握Java8新特性之Stream API的全面应用
    1.写在前面 关于Stream API的内容,已经基本上说完了。大家可以参考我的这两篇文章: 深入理解Java8新特性之Stream API的创建方式和中间操作步骤、深入理解Java...
    99+
    2024-04-02
  • Java8新特性之重复注解与类型注解详解
    目录Java8新特性重复注解与类型注解一、JDK5中的注解1.注解(@)2.作用3.如何理解注解?4.关于注解5.注解分为三个阶段6.注解的属性类型7.为注解增加属性二、Java8中...
    99+
    2024-04-02
  • 深入理解Java8新特性之Optional容器类的应用
    1.Optional容器类 Optional<T> 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,...
    99+
    2024-04-02
  • Java并发编程之线程安全性
    目录1.什么是线程安全性2.原子性2.1 竞争条件2.2 复合操作3.加锁机制3.1 内置锁3.2 重入4.用锁保护状态5.活跃性与性能1.什么是线程安全性 当多个线程访问某个类时,...
    99+
    2024-04-02
  • Java深入探索线程安全和线程通信的特性
    目录一、线程安全(重点)1、线程安全概念2、产生线程不安全的情况3、线程不安全的原因4、如何解决线程不安全问题二、synchronized关键字1、使用2、特性三、volatile关...
    99+
    2024-04-02
  • Java并发编程之线程安全性怎么实现
    今天小编给大家分享一下Java并发编程之线程安全性怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1.什么是线程安全性...
    99+
    2023-06-29
  • Oracle 12c新特性之多线程数据库的示例分析
    这篇文章将为大家详细讲解有关Oracle 12c新特性之多线程数据库的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。有一个概念,叫多进程和多线程。在Unix/Li...
    99+
    2024-04-02
  • Java 同步工具与组合类的线程安全性解析
    目录何为线程安全的类?基于条件的同步策略状态发布与所有权实例封闭正确地拓展同步策略同步容器复合操作不受同步容器保护同步容器的迭代问题警惕隐含迭代的操作并发容器ConcurrentHa...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作