• JDK8--09:全新的时间API


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

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

        public void test() throws Exception{
            //全新的时间API   都不是线程安全的
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
    
            Callable<Date> call = new Callable<Date>() {
                @Override
                public Date call() throws Exception {
                    return simpleDateFormat.parse("20200601");
                }
            };
    
            ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10);
    
            List<Future<Date>> list = new ArrayList<>();
    
            for(int i=0;i<10;i++){
                list.add(newFixedThreadPool.submit(call));
            }
    
            for (Future<Date> future : list){
                log.info("============={}",future.get());
            }
    
            newFixedThreadPool.shutdown();
        }

    运行结果:

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

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

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

    public class DateFormatThreadLocal {
        private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
            protected DateFormat initialValue() {
                return new SimpleDateFormat("yyyyMMdd");
            }
        };
    
        public static Date convert(String source) throws Exception{
            return df.get().parse(source);
        }
    }

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

        @Test
        public void test2() throws Exception{
            //全新的时间API   都不是线程安全的
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
    
            Callable<LocalDate> call = new Callable<LocalDate>() {
                @Override
                public LocalDate call() throws Exception {
                    return LocalDate.parse("20200601",dateTimeFormatter);
                }
            };
    
            ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10);
    
            List<Future<LocalDate>> list = new ArrayList<>();
    
            for(int i=0;i<10;i++){
                list.add(newFixedThreadPool.submit(call));
            }
    
            for (Future<LocalDate> future : list){
                log.info("============={}",future.get());
            }
    
            newFixedThreadPool.shutdown();
        }

    运行结果:

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

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

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

    1、时间对象创建

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

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

            //创建日期
            //LocalDate  LocalTime  LocalDateTime
            LocalDateTime ld = LocalDateTime.now();
            log.info("LocalDateTime.now()=================={}", ld);
    
            LocalDateTime ld1 = LocalDateTime.of(2020,5,31,13,25,36);
            log.info("LocalDateTime=================={}", ld1);

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

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

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

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

    4、时间戳操作

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

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

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

      (3)转换为时间戳

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

            //时间戳
            Instant instant = Instant.now();//默认是UTC时间(世界协调时间)
            log.info("时间戳===={}", instant);
            //转换为东八区时间(北京时间)
            OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
            log.info("转换为东八区时间(北京时间)=={}", offsetDateTime);
            //转换为时间戳
            long toEpochMilli = instant.toEpochMilli();
            log.info("时间戳=={}", toEpochMilli);
    
            //改变时间   距离时间戳的时间  1970-01-01 00:00:00
            Instant instant1 = Instant.ofEpochSecond(60*23);
            log.info("距离时间戳的时间=={}", instant1);

    运行结果:

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

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

    5、计算时间差

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

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

            //计算时间差
            //Duration:获取两个时间的时间差
            //Period:获取两个日期的间隔
    
            Instant instant2 = Instant.now();
            Thread.sleep(1000);
            Instant instant3 = Instant.now();
            Duration duration = Duration.between(instant2,instant3);
    
            log.info("duration.getSeconds()============={}", duration.getSeconds());
            log.info("duration.toMillis()============={}", duration.toMillis());
            log.info("duration.toHours()============={}", duration.toHours());
    
            LocalDate localDate = LocalDate.of(2018,10,6);
            LocalDate localDate1 = LocalDate.now();
            log.info("LocalDate.now()============={}", localDate1);
            Period period = Period.between(localDate,localDate1);
            log.info("period============={}", period);
            log.info("period.getYears()============={}", period.getYears());
            log.info("period.getMonths()============={}", period.getMonths());
            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未提供)

        /**
         * 时间矫正器
         */
        @Test
        public void test4(){
            //TemporalAdjuster:时间矫正器
            LocalDate localDate2 = LocalDate.now();
            log.info("LocalDate.now()============={}", localDate2);
            //指定时间
            LocalDate localDate3 = localDate2.withDayOfYear(55);
            log.info("withDayOfYear============={}", localDate3);
            //获取下一个周日
            LocalDate localDate4 = localDate2.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
            log.info("TemporalAdjuster============={}", localDate4);
            //自定义 下一个工作日
            LocalDate localDate5 = localDate2.with((x)->{
                LocalDate localDate6 = (LocalDate) x;
                DayOfWeek dayOfWeek = localDate6.getDayOfWeek();
                switch (dayOfWeek){
                    case FRIDAY:
                        return localDate6.plusDays(3);
                    case TUESDAY:
                        return localDate6.plusDays(2);
                    default:
                        return localDate6.plusDays(1);
                }
            });
            log.info("下一个工作日============={}", localDate5);
        }

    输出结果:

    2020-06-02 15:08:26.457  INFO 19392 --- [           main] com.example.jdk8demo.Jdk8demoTest2       : LocalDate.now()=============2020-06-02
    2020-06-02 15:08:26.461  INFO 19392 --- [           main] com.example.jdk8demo.Jdk8demoTest2       : withDayOfYear=============2020-02-24
    2020-06-02 15:08:26.463  INFO 19392 --- [           main] com.example.jdk8demo.Jdk8demoTest2       : TemporalAdjuster=============2020-06-07
    2020-06-02 15:08:26.464  INFO 19392 --- [           main] com.example.jdk8demo.Jdk8demoTest2       : 下一个工作日=============2020-06-04

    7、时间格式化

        /**
         * 时间日期格式化
         *
         */
        @Test
        public void test6(){
            DateTimeFormatter df = DateTimeFormatter.ISO_DATE_TIME;
            LocalDateTime ld = LocalDateTime.now();
            String date = ld.format(df);
            log.info("format==================={}",date);
    
            //自定义
            DateTimeFormatter df1 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
            date = ld.format(df1);
            log.info("format==================={}",date);
            //解析回原格式,此处要注意一点, HH时mm分ss秒大小写一定注意,错了就会反解析失败
            LocalDateTime localDateTime = ld.parse(date,df1);
            log.info("parse==================={}",localDateTime);
        }

    8、时区操作

      (1)获取所有时区

      (2)根据时区获取时间

      (3)计算时区的时间差

        /**
         * 时区操作
         * ZoneDate   ZoneTime    ZoneDateTime
         */
        @Test
        public void test7(){
            //获取所有时区
            Set<String> set = ZoneId.getAvailableZoneIds();
            log.info("ZoneId.getAvailableZoneIds()================={}",JSON.toJSONString(set));
            //根据时区获取时间
            LocalDateTime localDateTime = LocalDateTime.now(ZoneId.of("Pacific/Fiji"));
            LocalDateTime localDateTime1 = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
            log.info("根据时区获取日期=========Pacific/Fiji==={}==========Asia/Shanghai==={}",localDateTime, localDateTime1);
            //获取时差
            ZonedDateTime zonedDateTime = localDateTime1.atZone(ZoneId.of("Pacific/Fiji"));
            log.info("Shanghai与Fiji的时差========{}",zonedDateTime);
        }
  • 相关阅读:
    实验七---类的多态
    实验六
    实验五---排序、质数
    实验四---杨辉三角
    node中间件KOA函数
    java文件名判断练习
    npm install 安装报错错误问题
    bundle is not defined 手动搭建项目架构(一)
    ztree实现拖拽功能
    js单线程 详解 来自知乎
  • 原文地址:https://www.cnblogs.com/liconglong/p/13031387.html
Copyright © 2020-2023  润新知