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 ) & 不可变对象使用和设计 & 保护性拷贝的主要内容,如果未能解决你的问题,请参考以下文章