当前位置:网站首页>多线程之不可变对象
多线程之不可变对象
2022-08-08 17:40:00 【七国的天下,我要九十九】
1 日期转换的问题
SimpleDateFormat是线程不安全类,在对线程环境下,会出现错误.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
log.debug("{}", sdf.parse("2022-08-07"));
} catch (Exception e) {
log.error("{}", e);
}
}).start();
}
出现 java.lang.NumberFormatException 或者出现不正确的日期解析结果.
19:10:40.859 [Thread-2] c.TestDateParse - {
}
java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:601)
at java.lang.Long.parseLong(Long.java:631)
at java.text.DigitList.getLong(DigitList.java:195)
at java.text.DecimalFormat.parse(DecimalFormat.java:2084)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at cn.cf.n7.TestDateParse.lambda$test1$0(TestDateParse.java:18)
at java.lang.Thread.run(Thread.java:748)
2 解决方案
1 同步锁
加锁 ,保证每次只有一个线程去执行方法. 能解决问题,但是影响性能效率.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
for (int i = 0; i < 50; i++) {
new Thread(() -> {
synchronized (sdf) {
try {
log.debug("{}", sdf.parse("1951-04-21"));
} catch (Exception e) {
log.error("{}", e);
}
}
}).start();
}
2 类不可变
如果一个对象在不能够修改其内部状态(属性),那么它就是线程安全的,因为不存在并发修改.
以Java8提供的新日期格式化类DateTimeFormatter.
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
for (int i = 0; i < 10; i++) {
new Thread(() -> {
LocalDate date = dtf.parse("2022-08-07", LocalDate::from);
log.debug("{}", date);
}).start();
}
将对象置为不可变对象,是一种避免竞争的方式.
3 不可变设计
String类型就是不可变的,以其为例,整理不可变设计.
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
// ...
}
其中使用final修饰了String对象, 且属性都是final的.
- 属性用 final 修饰保证了该属性是只读的,不能修改
- 类用 final 修饰保证了该类中的方法不能被覆盖,防止子类无意间破坏不可变性
保护性拷贝
使用字符串时,也有一些跟修改相关的方法,比如 substring ,底层其实是同一个对象.
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
发现其内部是调用 String 的构造方法创建了一个新字符串,再进入这个构造看看,是否对 final char[] value 做出 了修改:
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) {
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
if (offset <= value.length) {
this.value = "".value;
return;
}
}
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
结果发现也没有,构造新字符串对象时,会生成新的 char[] value,对内容进行复制, 这种通过创建副本对象来避免共享的手段称为 保护性拷贝
边栏推荐
- Cuda Anaconda tensorflow 版本对应
- The difference between rv and sv
- Tensorflow教程(二)——基本概念与搭建流程
- 21天学习第四天--流程控制
- L2-020 功夫传人 (25 分)
- XDOJ - count the number of positive integers
- 使用电脑通过VNC Viewer远程连接树莓派4B
- How to set timed network disconnection to assist self-discipline in win10
- Tensorflow教程(六)——变量基础操作
- 无需精子卵子子宫体外培育胚胎,Cell论文作者这番话让网友们炸了
猜你喜欢
随机推荐
爬百度图片
Tensorflow教程(二)——基本概念与搭建流程
orbslam2实验记录-----稠密建图
三年软件工程真题
从2022投影行业最新报告,读懂2022年家用智能投影仪该怎么选!
咸阳广发证券股票开户安全吗,需要准备什么证件
中金证券股票开户流程是什么,我需要准备身份证吗,安全吗
win10如何设置定时联网断网辅助自律
【教程2】疯壳·ARM功能手机-测试程序介绍
发光的几何图形canvasjs特效
比较器是否可以当做运放使用?
C人脸识别
L2-013 红色警报 (25 分)(并查集)
2 prerequisites for the success of "digital transformation" of enterprises!
21天学习第六天--方法
目标检测、目标跟踪、图像分类最新进展
@Transactional
在指南针炒股软件中的指标靠谱吗?安全吗?
L2-022 重排链表 (25 分)(模拟链表)
1dp到底多大!