我的编程空间,编程开发者的网络收藏夹
学习永远不晚

怎么在Java8中实现一个线程安全日期类

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

怎么在Java8中实现一个线程安全日期类

这期内容当中小编将会给大家带来有关怎么在Java8中实现一个线程安全日期类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

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类(新)
DATETIMEjava.util.DateLocalDateTime
DATE  java.sql.DateLocalDate
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 month2 = 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线程安全的?为什么?

源码:

怎么在Java8中实现一个线程安全日期类

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

format和parse线程安全替代

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

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

怎么在Java8中实现一个线程安全日期类

上述就是小编为大家分享的怎么在Java8中实现一个线程安全日期类了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

怎么在Java8中实现一个线程安全日期类

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

怎么在Java8中实现一个线程安全日期类

这期内容当中小编将会给大家带来有关怎么在Java8中实现一个线程安全日期类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。LocalDateTimeJava8新特性之一,新增日期类。在项目开发过程中经常遇到
2023-06-14

怎么在Java8项目中实现一个函数式编程

这期内容当中小编将会给大家带来有关怎么在Java8项目中实现一个函数式编程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。函数式的思考命令式编程一般我们实现一个系统有两种思考方式,一种专注于如何实现,比如下
2023-05-31

怎么在Python中利用tkinter实现一个日期选择器

这篇文章主要介绍了怎么在Python中利用tkinter实现一个日期选择器,此处通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考价值,需要的朋友可以参考下:首先,定义一个类,叫Calendar# -*- coding: u
2023-06-06

Java中怎么实现线程安全

Java中怎么实现线程安全,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。1、使用synchronized关键字synchronized关键字可以修饰方法和代码块,它的语义是
2023-06-16

怎么在java中实现一个多线程爬虫

怎么在java中实现一个多线程爬虫?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。Java可以用来干什么Java主要应用于:1. web开发;2. Android
2023-06-14

怎么在java中利用Runnable实现一个线程

怎么在java中利用Runnable实现一个线程?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Java是什么Java是一门面向对象编程语言,可以编写桌面应用程序、Web应用
2023-06-14

怎么在Java8中使用Collectors实现一个求和功能

怎么在Java8中使用Collectors实现一个求和功能?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。自定义工具类public class MyCollectors { p
2023-06-06

C#中怎么实现多线程安全

C#中怎么实现多线程安全,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。C#多线程控制进度条之多线程安全的问题:我们知道 Windows 编程中有一个必须遵守的原则,那就是在一个
2023-06-17

java中怎么实现一个线程池

本篇文章为大家展示了java中怎么实现一个线程池,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。有些人可能对线程池比较陌生,并且更不熟悉线程池的工作原理。所以他们在使用线程的时候,多数情况下都是new
2023-06-20

Java中怎么实现一个多线程程序

Java中怎么实现一个多线程程序,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。Java多线程程序设计详细解析一、理解Java多线程Java多线程是这样一种机制,它允许在程序中并
2023-06-17

Python中如何实现一个线程安全的缓存对象

Python中如何实现一个线程安全的缓存对象随着多线程编程在Python中的越来越被广泛应用,线程安全性变得愈发重要。在并发环境中,多个线程同时读写共享资源时,可能会导致数据不一致或者意外的结果。为了解决这个问题,我们可以使用线程安全的缓存
2023-10-22

android开发中怎么实现一个日期选择控件

android开发中怎么实现一个日期选择控件?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。效果如下:具体实现方法为:先新建一个安卓项目DoubleDatePicker,在res
2023-05-31

Java中怎么安全组建一个系统线程组

今天就跟大家聊聊有关Java中怎么安全组建一个系统线程组,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。在 Java 中,除系统线程组外的每个线程组又隶属于另一个线程组,你可以在创建线
2023-06-17

怎么在Android中实现一个多线程下载功能

怎么在Android中实现一个多线程下载功能?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。1、布局 界面上自上而下放置一个TextView,用来提示文本框中输
2023-05-30

Java中ConcurrentHashMap是怎么实现线程安全的

这篇文章主要介绍“Java中ConcurrentHashMap是怎么实现线程安全的”,在日常操作中,相信很多人在Java中ConcurrentHashMap是怎么实现线程安全的问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希
2023-06-25

Java中怎么利用Calendar类实现一个动态日历

本篇文章为大家展示了Java中怎么利用Calendar类实现一个动态日历,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。1.打印日历1.1当月总天数1.2当月第一天是周几(注意:月份是从0开始,星期是
2023-06-20

怎么在java中创建一个接口实现类

这期内容当中小编将会给大家带来有关怎么在java中创建一个接口实现类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。java基本数据类型有哪些Java的基本数据类型分为:1、整数类型,用来表示整数的数据类型
2023-06-14

c++中怎么实现一个对象运行多个线程

在C++中,可以使用线程库来实现一个对象运行多个线程。下面是一个简单的示例:#include #include class MyObject {public:void threadFunc(int id) {for(int i = 0
2023-10-25

C++中怎么实现一个多线程计数器

C++中怎么实现一个多线程计数器,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。以计数器实现为例子,演示了多线程计数器的实现技术方法,代码如下://目的: 测试利
2023-06-17

怎么在ccs3中使用timeline实现一个时间线

本篇文章为大家展示了怎么在ccs3中使用timeline实现一个时间线,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。1、test.html文件2023-06-08

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录