JUC并发编程 -- 日期转换的问题(SimpleDateFormat ) & 不可变对象使用和设计 & 保护性拷贝

Posted Z && Y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUC并发编程 -- 日期转换的问题(SimpleDateFormat ) & 不可变对象使用和设计 & 保护性拷贝相关的知识,希望对你有一定的参考价值。

1. 日期转换的问题

问题提出:

下面的代码在运行时,由于 SimpleDateFormat 不是线程安全的,有很大几率出现 java.lang.NumberFormatException 或者出现不正确的日期解析结果,例如:

@Slf4j(topic = "c.Test1")
public class Test1 {
    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    log.debug("{}", sdf.parse("1951-04-21"));
                } catch (Exception e) {
                    log.error("{}", e);
                }
            }).start();
        }
    }

运行结果:


2. 解决方法


2.1 synchronized加锁

测试代码:

@Slf4j(topic = "c.Test1")
public class Test1 {
    public static void main(String[] args) {
        test();
    }

    private static void test() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                synchronized (sdf) {
                    try {
                        log.debug("{}", sdf.parse("1951-04-21"));
                    } catch (Exception e) {
                        log.error("{}", e);
                    }
                }
            }).start();
        }
    }

运行结果:


2.2 不可变对象使用

DateTimeFormatter类是线程安全的类

示例代码:

@Slf4j(topic = "c.Test1")
public class Test1 {
    public static void main(String[] args) {
        DateTimeFormatter stf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                TemporalAccessor parse = stf.parse("1951-04-21");
                log.debug("{}", parse);
            }).start();
        }
    }

运行结果:


3. 不可变对象的设计

String 类也是不可变的,以它为例,说明一下不可变设计的要素


3.1 说明1: final 的使用

发现该类、类中所有属性都是 final 的

  • 属性用 final 修饰保证了该属性是只读的,不能修改(“引用地址不可变”
  • 类用 final 修饰保证了该类中的方法不能被覆盖,防止子类无意间破坏不可变性

3.2 说明2: 保护性拷贝

但有同学会说,使用字符串时,也有一些跟修改相关的方法啊,比如 substring 等,那么下面就看一看这些方法是如何实现的,就以 substring 为例:


发现其内部是调用 String 的构造方法创建了一个新字符串,再进入这个构造看看,是否对 final char[] value 做出了修改:

结果发现也没有,构造新字符串对象时,会生成新的 char[] value,对内容进行复制 。这种通过创建副本对象来避免共享的手段称之为【保护性拷贝(defensive copy)】



以上是关于JUC并发编程 -- 日期转换的问题(SimpleDateFormat ) & 不可变对象使用和设计 & 保护性拷贝的主要内容,如果未能解决你的问题,请参考以下文章

Java并发编程系列之三JUC概述

JUC 高并发编程

juc多线程编程学习

JUC并发编程总结复盘

JUC并发编程 -- 线程的五种状态( 操作系统层面) 和 六种状态(Java API层面)

多线程进阶=>JUC并发编程