这是我参与更文挑战的第13天,活动详情查看: 更文挑战
线程不安全验证
SimpleDateFormat
线程不安全验证实验:
package cn.xxxx.concurrentdate;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ConcurrentDateTest {
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
private static String date1String = "2010-03-04";
//private static String date2String = "2013-04-05";
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Date date1 = simpleDateFormat.parse(date1String);
//String date1S = simpleDateFormat.format(date1);
System.out.println(Thread.currentThread().getName() + ":" +date1);
} catch (Exception e) {
System.out.println("Thread error");
//throw new RuntimeException("parse failed", e);
}
}
}).start();
}
}
}
复制代码
输出结果:
Thread error
Thread error
Thread-1:Sat Mar 04 00:00:00 CST 2220
Thread-9:Thu Mar 04 00:00:00 CST 2010
Thread-6:Thu Mar 04 00:00:00 CST 2010
Thread-4:Thu Mar 04 00:00:00 CST 2010
Thread-3:Thu Mar 04 00:00:00 CST 2010
Thread-7:Thu Mar 04 00:00:00 CST 2010
Thread-8:Thu Mar 04 00:00:00 CST 2010
Thread-5:Thu Mar 04 00:00:00 CST 2010
复制代码
执行结果表明,日期要么是错的,要么发生了异常,SimpleDateFormat
存在线程安全问题。
解决办法:
1. 加锁
线程内:synchronized(simpleDateFormat) { ..do parse and format. }:
这种方式会存在性能问题。比如Web程序,1000个用户同时访问一个涉及到时间的接口,不应该因SimpleDateFormat
造成并发转串行的严重性能问题。
2. 定义线程局部变量
变量是同一个,但是每个线程使用同一个初始值,存取数据只在本线程内有效(内部通过一个Map(实际是内部类ThreadLocalMap)存取数据,存取数据只在同一线程有效)
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
@Override
protected DateFormat initialValue(){
return new SimpleDateFormat("yyyy-MM-dd");
}
};
复制代码
Java 8+以上可以用更简便的lambda表达式:
public static final ThreadLocal<DateFormat> df = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
复制代码
使用方法:
new Thread(new Runnable() {
@Override
public void run() {
try {
Date date1 = df.get().parse(date1String);
System.out.println(Thread.currentThread().getName() + ":" +date1);
} catch (Exception e) {
System.out.println("Thread error");
}
}
}).start();
复制代码
3. jdk8推荐方法:
使用 Instant 代替 Date, LocalDateTime 代替 Calendar,
DateTimeFormatter 代替 SimpleDateFormat,官方给出的解释: simple beautiful strong
immutable thread-safe。
变量转换方式如下所示:参考[1] [2]
/*转Instant*/
Instant dateInstant = date.toInstant();
//Timestamp转Instant
Instant timestampInstant = timestamp.toInstant();
//Calendar转Instant
Instant calendarInstant = calendar.toInstant();
//LocalDateTime转Instant
Instant localDateTimeInstant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
//ZonedDateTime转Instant
Instant zonedDateTimeInstant = zonedDateTime.toInstant();
//LocalDate转Instant
Instant localDateInstant = localDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
/*转LocalDateTime*/
//Date转LocalDateTime
LocalDateTime dateLocalDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
//Timestamp转LocalDateTime
LocalDateTime timestampLocalDateTime = timestamp.toLocalDateTime();
//Calendar转LocalDateTime
LocalDateTime calendarLocalDateTime = LocalDateTime.ofInstant(calendar.toInstant(), ZoneOffset.systemDefault());
//Instant转LocalDateTime
LocalDateTime instantLocalDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
//ZonedDateTime转LocalDateTime
LocalDateTime zonedDateTimeLocalDateTime = zonedDateTime.toLocalDateTime();
//LocalDate转LocalDateTime
LocalDateTime localDateLocalDateTime = localDate.atStartOfDay();
/*转LocalDate*/
// date转LocalDate
LocalDate dateLocalDate = LocalDate.ofInstant(date.toInstant(), ZoneId.systemDefault()); //jdk11
LocalDate dateLocalDate = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).toLocalDate();
//Timestamp转LocalDate
LocalDate timestampLocalDate = timestamp.toLocalDateTime().toLocalDate();
//Calendar转LocalDate
LocalDate calendarLocalDate = LocalDate.ofInstant(calendar.toInstant(), ZoneOffset.systemDefault()); //jdk11
LocalDate calendarLocalDate = LocalDateTime.ofInstant(calendar.toInstant(), ZoneOffset.systemDefault()).toLocalDate();
//Instant转LocalDate
LocalDate instantLocalDate = LocalDate.ofInstant(instant, ZoneId.systemDefault()); //jdk11
LocalDate instantLocalDate = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalDate();
//LocalDateTime转LocalDate
LocalDate localDateTimeLocalDate = localDateTime.toLocalDate();
//ZonedDateTime转LocalDate
LocalDate zonedDateTimeLocalDate = zonedDateTime.toLocalDate();
复制代码
[1] JSR310 时间类型的相互转换
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END