在JDK8之前,时间有各种问题,最大的问题就是,我们使用的时间格式化类SimpleDateFormat不是线程安全的

为了更准确的说明SimpleDateFormat非线程安全,演示一个并发做时间格式化的操作

  1. public void test() throws Exception{
  2. //全新的时间API 都不是线程安全的
  3. SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
  4.  
  5. Callable<Date> call = new Callable<Date>() {
  6. @Override
  7. public Date call() throws Exception {
  8. return simpleDateFormat.parse("");
  9. }
  10. };
  11.  
  12. ExecutorService newFixedThreadPool = Executors.newFixedThreadPool();
  13.  
  14. List<Future<Date>> list = new ArrayList<>();
  15.  
  16. for(int i=;i<;i++){
  17. list.add(newFixedThreadPool.submit(call));
  18. }
  19.  
  20. for (Future<Date> future : list){
  21. log.info("============={}",future.get());
  22. }
  23.  
  24. newFixedThreadPool.shutdown();
  25. }

运行结果:

就是因为多个线程并发使用SimpleDateFormat,因此出现了异常,那么JDK8之前我们是如何保证SimpleDateFormat线程安全的呢?

在JDK8之前,我们使用ThreadLocal锁定SimpleDateFormat,来保证线程安全:

首先,创建一个使用TreadLocal锁定的SimpleDateFormat

  1. public class DateFormatThreadLocal {
  2. private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
  3. protected DateFormat initialValue() {
  4. return new SimpleDateFormat("yyyyMMdd");
  5. }
  6. };
  7.  
  8. public static Date convert(String source) throws Exception{
  9. return df.get().parse(source);
  10. }
  11. }

其次,在做时间格式化时,使用该类中锁定的SimpleDateFormat对象

  1. @Test
  2. public void test2() throws Exception{
  3. //全新的时间API 都不是线程安全的
  4. DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
  5.  
  6. Callable<LocalDate> call = new Callable<LocalDate>() {
  7. @Override
  8. public LocalDate call() throws Exception {
  9. return LocalDate.parse("",dateTimeFormatter);
  10. }
  11. };
  12.  
  13. ExecutorService newFixedThreadPool = Executors.newFixedThreadPool();
  14.  
  15. List<Future<LocalDate>> list = new ArrayList<>();
  16.  
  17. for(int i=;i<;i++){
  18. list.add(newFixedThreadPool.submit(call));
  19. }
  20.  
  21. for (Future<LocalDate> future : list){
  22. log.info("============={}",future.get());
  23. }
  24.  
  25. newFixedThreadPool.shutdown();
  26. }

运行结果:

可以发现,程序运行正常,均做了格式化

那么在JDK8中,提供了全新的时间类,这些类都是线程安全的,可以在多线程并发时使用,无需担心线程安全问题,无需创建ThreadLocal锁定的格式化类

首先,介绍JDK8中新增的时间类 :LocalDate LocalTime LocalDateTime

1、时间对象创建

  (1)可以使用now创建时间类,时间为当前时间

    (2)可以使用of创建指定时间

  1. //创建日期
  2. //LocalDate LocalTime LocalDateTime
  3. LocalDateTime ld = LocalDateTime.now();
  4. log.info("LocalDateTime.now()=================={}", ld);
  5.  
  6. LocalDateTime ld1 = LocalDateTime.of(,,,,,);
  7. log.info("LocalDateTime=================={}", ld1);

2、日期运算:JDK8提供了plus*方法可以对日期进行运算操作

  1. //日期运算 plus 加日期
  2. LocalDateTime ld = LocalDateTime.now();
  3. log.info("plusDays=================={}", ld.plusDays());
  4. log.info("plusHours=================={}", ld.plusHours());

3、get返回值:JDK8提供了get*方法可以返回年份、月份、日期等

  1. //get 返回值
  2. LocalDateTime ld = LocalDateTime.now();
  3. log.info("getMonthValue=================={}", ld.getMonthValue());
  4. log.info("getDayOfMonth=================={}", ld.getDayOfMonth());

4、时间戳操作

  上面提到的LocalDate、LocalTime、LocalDateTime输出的都是指定的时间格式,但是如果我们需要使用时间戳,就需要其他的方式处理,对于时间戳的操作如下:

  (1)直接获取时间戳,此时获取的时间戳为UTC时间,即世界协调时间(世界标准时间,以北京时间为例,由于北京时间采取的是东八区时间,因此是标准时间+8个小时,即为北京时间)

  (2)获取指定时区的时间戳

  (3)转换为时间戳

  (4)指定时间戳起始时间(1970-01-01 00:00:00)后多少时间的时间戳

  1. //时间戳
  2. Instant instant = Instant.now();//默认是UTC时间(世界协调时间)
  3. log.info("时间戳===={}", instant);
  4. //转换为东八区时间(北京时间)
  5. OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours());
  6. log.info("转换为东八区时间(北京时间)=={}", offsetDateTime);
  7. //转换为时间戳
  8. long toEpochMilli = instant.toEpochMilli();
  9. log.info("时间戳=={}", toEpochMilli);
  10.  
  11. //改变时间 距离时间戳的时间 1970-01-01 00:00:00
  12. Instant instant1 = Instant.ofEpochSecond(*);
  13. log.info("距离时间戳的时间=={}", instant1);

运行结果:

通过运行结果可以发现,时间戳和根据时区获取的时间戳输出时,仍然是格式化过的时间格式,且是由使用东八区的时间时,才与我电脑上的北京时间一致,同时输出的时间上有+08:00的输出;

另外,对于最后一个距离时间戳的时间,我们设置了60*23,即设置了23分钟,最后输出时间为:1970-01-01T00:23:00Z

5、计算时间差

  (1)Duration:获取两个时间的时间差

  (2)Period:获取两个日期的间隔

  1. //计算时间差
  2. //Duration:获取两个时间的时间差
  3. //Period:获取两个日期的间隔
  4.  
  5. Instant instant2 = Instant.now();
  6. Thread.sleep();
  7. Instant instant3 = Instant.now();
  8. Duration duration = Duration.between(instant2,instant3);
  9.  
  10. log.info("duration.getSeconds()============={}", duration.getSeconds());
  11. log.info("duration.toMillis()============={}", duration.toMillis());
  12. log.info("duration.toHours()============={}", duration.toHours());
  13.  
  14. LocalDate localDate = LocalDate.of(,,);
  15. LocalDate localDate1 = LocalDate.now();
  16. log.info("LocalDate.now()============={}", localDate1);
  17. Period period = Period.between(localDate,localDate1);
  18. log.info("period============={}", period);
  19. log.info("period.getYears()============={}", period.getYears());
  20. log.info("period.getMonths()============={}", period.getMonths());
  21. log.info("period.getDays()============={}", period.getDays());

运行结果:

  (1)我们在代码中,让程序休眠了1秒,因此两个时间相差的秒数为1,毫秒数为1001是因为程序运行使用了1毫秒

  (2)计算日期差时,我们使用了指定日期2018.10.6和当前日期(2020.6.2)做比较,输出为P1Y7M27D,表示差了1年7个月27天,然后使用get*获取时,获取的即这个输出的年、月、天

6、时间矫正器

  (1)可以获取指定该年中的天数

  (2)获取下一个指定的日期(下一个周日,JDK已提供)

  (3)获取下一个工作日/结婚纪念日等(JDK未提供)

  1. /**
  2. * 时间矫正器
  3. */
  4. @Test
  5. public void test4(){
  6. //TemporalAdjuster:时间矫正器
  7. LocalDate localDate2 = LocalDate.now();
  8. log.info("LocalDate.now()============={}", localDate2);
  9. //指定时间
  10. LocalDate localDate3 = localDate2.withDayOfYear();
  11. log.info("withDayOfYear============={}", localDate3);
  12. //获取下一个周日
  13. LocalDate localDate4 = localDate2.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
  14. log.info("TemporalAdjuster============={}", localDate4);
  15. //自定义 下一个工作日
  16. LocalDate localDate5 = localDate2.with((x)->{
  17. LocalDate localDate6 = (LocalDate) x;
  18. DayOfWeek dayOfWeek = localDate6.getDayOfWeek();
  19. switch (dayOfWeek){
  20. case FRIDAY:
  21. return localDate6.plusDays();
  22. case TUESDAY:
  23. return localDate6.plusDays();
  24. default:
  25. return localDate6.plusDays();
  26. }
  27. });
  28. log.info("下一个工作日============={}", localDate5);
  29. }

输出结果:

  1. -- ::26.457 INFO --- [ main] com.example.jdk8demo.Jdk8demoTest2 : LocalDate.now()=============--
  2. -- ::26.461 INFO --- [ main] com.example.jdk8demo.Jdk8demoTest2 : withDayOfYear=============--
  3. -- ::26.463 INFO --- [ main] com.example.jdk8demo.Jdk8demoTest2 : TemporalAdjuster=============--
  4. -- ::26.464 INFO --- [ main] com.example.jdk8demo.Jdk8demoTest2 : 下一个工作日=============--

7、时间格式化

  1. /**
  2. * 时间日期格式化
  3. *
  4. */
  5. @Test
  6. public void test6(){
  7. DateTimeFormatter df = DateTimeFormatter.ISO_DATE_TIME;
  8. LocalDateTime ld = LocalDateTime.now();
  9. String date = ld.format(df);
  10. log.info("format==================={}",date);
  11.  
  12. //自定义
  13. DateTimeFormatter df1 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
  14. date = ld.format(df1);
  15. log.info("format==================={}",date);
  16. //解析回原格式,此处要注意一点, HH时mm分ss秒大小写一定注意,错了就会反解析失败
  17. LocalDateTime localDateTime = ld.parse(date,df1);
  18. log.info("parse==================={}",localDateTime);
  19. }

8、时区操作

  (1)获取所有时区

  (2)根据时区获取时间

  (3)计算时区的时间差

  1. /**
  2. * 时区操作
  3. * ZoneDate ZoneTime ZoneDateTime
  4. */
  5. @Test
  6. public void test7(){
  7. //获取所有时区
  8. Set<String> set = ZoneId.getAvailableZoneIds();
  9. log.info("ZoneId.getAvailableZoneIds()================={}",JSON.toJSONString(set));
  10. //根据时区获取时间
  11. LocalDateTime localDateTime = LocalDateTime.now(ZoneId.of("Pacific/Fiji"));
  12. LocalDateTime localDateTime1 = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
  13. log.info("根据时区获取日期=========Pacific/Fiji==={}==========Asia/Shanghai==={}",localDateTime, localDateTime1);
  14. //获取时差
  15. ZonedDateTime zonedDateTime = localDateTime1.atZone(ZoneId.of("Pacific/Fiji"));
  16. log.info("Shanghai与Fiji的时差========{}",zonedDateTime);
  17. }

JDK8--09:全新的时间API的更多相关文章

  1. JDK8 新增的日期时间API

    背景 JDK8中增加了一套全新的日期时间API,这里进行总结下,方便查询使用. 新的时间及日期API位于 java.time 包中,下面是一些关键类. Instant:代表的是时间戳. LocalDa ...

  2. JDK8中的新时间API:Duration Period和ChronoUnit介绍

    目录 简介 Duration Period ChronoUnit 简介 在JDK8中,引入了三个非常有用的时间相关的API:Duration,Period和ChronoUnit. 他们都是用来对时间进 ...

  3. JDK8中新日期时间API

    它们面临的问题是:可变性:像日期和时间这样的类应该是不可变的.偏移性:Date中的年份是从1900开始的,而月份都从0开始.格式化:格式化只对Date有用,Calendar则不行.此外,它们也不是线程 ...

  4. java 数据结构(三):java常用类 三 日期时间API

    JDK 8之前日期时间API 1.获取系统当前时间:System类中的currentTimeMillis()long time = System.currentTimeMillis();//返回当前时 ...

  5. Java 常用类-程序员头大的日期时间API

    第二节.日期时间API 一.JDK8之前日期时间API 1.1 java.lang.System类 System类提供的public static long currentTimeMillis()用来 ...

  6. Day029 JDK8中新日期和时间API (二)

    # JDK8中新日期和时间API (二) Instant介绍 Instant:时间线上的一个瞬时点. 这可能被用来记录应用程序中的事件时间 戳. 在处理时间和日期的时候,我们通常会想到年,月,日,时, ...

  7. Day029 JDK8中新日期和时间API (四)

    JDK8中新日期和时间API 其他的一些API ZoneId:该类中包含了所有的时区信息,一个时区的ID,如 Europe/Paris ZonedDateTime:一个在ISO-8601日历系统时区的 ...

  8. JDK8中的时间API

    在Java 1.0中,对日期和时间的支持只能依赖java.util.Date类.正如类名所表达的,这个类无法表示日期,只能以毫秒的精度表示时间.更糟糕的是它的易用性,由于某些原因未知的设计决策,这个类 ...

  9. Java8 时间 API

    前言 Java8 中最为人津津乐道的新改变恐怕当属函数式 API 的加入.但实际上,Java8 所加入的新功能远不止这个. 本文将基于<Java SE8 for the Really Impat ...

随机推荐

  1. Java实现 LeetCode 730 统计不同回文子字符串(动态规划)

    730. 统计不同回文子字符串 给定一个字符串 S,找出 S 中不同的非空回文子序列个数,并返回该数字与 10^9 + 7 的模. 通过从 S 中删除 0 个或多个字符来获得子字符序列. 如果一个字符 ...

  2. Java实现 LeetCode 508 出现次数最多的子树元素和

    508. 出现次数最多的子树元素和 给出二叉树的根,找出出现次数最多的子树元素和.一个结点的子树元素和定义为以该结点为根的二叉树上所有结点的元素之和(包括结点本身).然后求出出现次数最多的子树元素和. ...

  3. Java实现 蓝桥杯 算法训练 谁干的好事?

    试题 算法训练 谁干的好事? 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 ABCDE中num个人做了好事,truth个人说真话. A说:"我和X中有且只有一个做了好事& ...

  4. Java实现预排序

    1 问题描述 在计算机科学中,预排序是一种很古老的思想.实际上,对于排序算法的兴趣很大程度上是因为这样一个事实:如果列表是有序的,许多关于列表的问题更容易求解.显然,对于包含了排序操作,这种算法的时间 ...

  5. java实现 洛谷 P1017 进制转换

    import java.util.Scanner; public class Main { private static Scanner cin; public static void main(St ...

  6. java实现第四届蓝桥杯世纪末星期

    世纪末星期 题目描述 曾有邪教称1999年12月31日是世界末日.当然该谣言已经不攻自破. 还有人称今后的某个世纪末的12月31日,如果是星期一则会- 有趣的是,任何一个世纪末的年份的12月31日都不 ...

  7. Jmeter连接数据库进行参数化

    实际使用Jmeter进行性能测试或接口测试自动化过程中,很多场景需要从数据库中获取一些关键性参数,或进行一些断言,比较,那么如何进行数据库连接以及怎么获取参数就变得尤为重要 一.下载mysql驱动 1 ...

  8. (四)SQLMap之Tamper篡改脚本的类型、作用、适用场景

    目录结构 一.SQLMap中tamper的简介 1.tamper的作用 2.tamper用法 二.适配不同数据库类型的测试tamper 三.SQLMap中tamper篡改脚本的功能解释 一.SQLMa ...

  9. (二)SQL注入常用的内置函数整理(以MySql为例)

    [1]@@datadir 函数作用:返回数据库的存储目录构造SQL语句 select @@datadir;   [2]@@version_compile_os 函数作用:查看服务器的操作系统SQL语句 ...

  10. Spring源码之自动装配

    我们使用Spring开发过程中经常会用到Autowired注解注入依赖的bean,这部分也是面试的热点问题之一.今天咱们一起来深入研究下自动注入的背后实现原理.首先上一个例子,如下所示: @RestC ...