zoukankan      html  css  js  c++  java
  • Java日期格式化之线程安全

    在项目中或多或少会用到日期格式。如果在单线程中,可以不用考虑使用的格式化类是否线程安全,但是在多线程,并发执行时,就要考虑线程同步的问题了。

    下面提供四中解决方式,并简单说明一下优缺点(看注释)

    ConcurrentDateFormat 和 ThreadLocalDateFormat 是自己封装的

    import org.junit.Test;
    
    import java.text.SimpleDateFormat;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 验证 SimpleDateFormat 是线程不安全的
     */
    public class DateFormat {
    
        /**
         * 只创建一份,避免频繁地创建对象和销毁对象
         * 单线程下可以不出错
         * 多线程下则不安全, 不同的线程会对不同日期字符串进行解析,
         * 会出现线程 A 解析到一半被挂起,线程 B 运行时将 A 的解析到一半的字符串覆盖掉,
         * 这样轮到 A 运行时会解析失败,或者使用了 B 的字符串
         */
        private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        /**
         * 线程安全的 DateTimeFormatter
         * 推荐使用,因为该类是不可变的,并且是线程安全的
         */
        private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    
        @Test
        public void formatTest() {
            ExecutorService service = Executors.newFixedThreadPool(10);
    
            for(int i = 0; i < 10; i++){
                service.execute(new Runnable() {
                    @Override
                    public void run() {
                        String dateStr = "2019-04-16 10:26:30";
                        // 解决方法
                        // 1、可以只在需要时创建对象,也可以避免错误,但是频繁创建与销毁会导致额外的开销,性能低
    //                    SimpleDateFormat newInNeed = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        for(int j = 0; j < 10; j++){
    //
    //                        try {
                                // 直接使用不但运行结果错误,最后还会抛出 NumberFormatException: 异常
    //                            System.out.println(format.parse(dateStr));
    
                                // 2、使用加了 synchronized 的同步方法,但是并发量高时,性能影响大,线程阻塞
    //                            System.out.println(ConcurrentDateFormat.parse(dateStr));
    
                                // 3、使用 ThreadLocal 来解决,比较优雅的一种做法
    //                            System.out.println(ThreadLocalDateFormat.parse(dateStr));
    
    //                        } catch (ParseException e) {
    //                            e.printStackTrace();
    //                        }
                            // 4、使用DateFormatter,该类是线程安全的,可以放心使用
                            LocalDateTime localDateTime = LocalDateTime.parse(dateStr, dtf);
                            System.out.println(localDateTime);
                        }
                    }
                });
            }
        }
    }
    

      

    ConcurrentDateFormat.java

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * 通过 synchronized 做一个线程安全的DataFormat
     */
    public class ConcurrentDateFormat {
        /**
         * 依旧只创建一个 SimpleFormat 对象
         */
        private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        // 采用加锁的方式,防止同步问题
        public static String format(Date date){
            synchronized (sdf){
                return sdf.format(date);
            }
        }
    
        public static Date parse(String date) throws ParseException {
            synchronized (sdf){
                return sdf.parse(date);
            }
        }
    
    }
    

      

    ThreadLocalDateFormat.java

    import java.text.DateFormat;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * 通过 ThreadLocal 为每个线程做一份变量副本,实现线程安全
     */
    public class ThreadLocalDateFormat {
    
        /**
         * ThreadLocal 提供一种 lombda 构造方式
         * 返回此线程局部变量的当前线程的“初始值”。线程第一次使用 get() 方法访问变量时将调用此方法,
         * 但如果线程之前调用了 set(T) 方法,则不会对该线程再调用 initialValue 方法。通常,此方法
         * 对每个线程最多调用一次,但如果在调用 get() 后又调用了 remove(),则可能再次调用此方法。
         */
        private static ThreadLocal<DateFormat> threadLocal
                = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    
        public static Date parse(String date) throws ParseException {
            System.out.println(date);
            return threadLocal.get().parse(date);
        }
    
        public static String format(Date date){
            return threadLocal.get().format(date);
        }
    }
    

      


    原文:https://blog.csdn.net/innocent_jia/article/details/89331045

  • 相关阅读:
    工作中遇到的java 内存溢出,问题排查
    java线上内存溢出问题排查步骤
    性能测试-java内存溢出问题排查
    164 01 Android 零基础入门 03 Java常用工具类01 Java异常 04 使用try…catch…finally实现异常处理 04 终止finally执行的方法
    163 01 Android 零基础入门 03 Java常用工具类01 Java异常 04 使用try…catch…finally实现异常处理 03 使用多重catch结构处理异常
    162 01 Android 零基础入门 03 Java常用工具类01 Java异常 04 使用try…catch…finally实现异常处理 02 使用try-catch结构处理异常
    161 01 Android 零基础入门 03 Java常用工具类01 Java异常 04 使用try…catch…finally实现异常处理 01 try-catch-finally简介
    160 01 Android 零基础入门 03 Java常用工具类01 Java异常 03 异常处理简介 01 异常处理分类
    159 01 Android 零基础入门 03 Java常用工具类01 Java异常 02 异常概述 02 异常分类
    158 01 Android 零基础入门 03 Java常用工具类01 Java异常 02 异常概述 01 什么是异常?
  • 原文地址:https://www.cnblogs.com/qbdj/p/10877062.html
Copyright © 2011-2022 走看看