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

  • 相关阅读:
    Ubuntu16.04 Jupyter安装(Ipython Notebook安装)
    爬虫.requests.exceptions.ConnectionErro
    预习非数值数据的编码方式
    预习(原码,补码,移码)+作业
    C语言ll作业01
    C语言寒假大作战04
    C语言寒假大作战03
    C语言寒假大作战02
    C语言寒假大作战01
    C语言1博客作业12
  • 原文地址:https://www.cnblogs.com/qbdj/p/10877062.html
Copyright © 2011-2022 走看看