This is the 13th day of my participation in Gwen Challenge
Thread unsafe validation
SimpleDateFormat Thread unsafe validation experiment:
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(a) {
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(); }}}Copy the code
Output result:
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
Copy the code
The results show that the date is either wrong or an exception has occurred, and SimpleDateFormat has a thread-safety issue.
Solutions:
1. The lock
Synchronized (simpleDateFormat) {.. do parse and format. }:
There are performance issues with this approach. For Web applications, for example, 1000 users accessing a time-dependent interface at the same time should not cause serious performance problems with concurrent serialization due to SimpleDateFormat.
2. Define thread-local variables
The variables are the same, but each thread uses the same initial value. Accessing data is only valid within the thread.
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
@Override
protected DateFormat initialValue(a){
return new SimpleDateFormat("yyyy-MM-dd"); }};Copy the code
Java 8+ + can be used with a simpler lambda expression:
public static final ThreadLocal<DateFormat> df = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
Copy the code
Usage:
new Thread(new Runnable() {
@Override
public void run(a) {
try {
Date date1 = df.get().parse(date1String);
System.out.println(Thread.currentThread().getName() + ":" +date1);
} catch (Exception e) {
System.out.println("Thread error");
}
}
}).start();
Copy the code
3. Jdk8 recommended methods:
Use Instant instead of Date, LocalDateTime instead of Calendar, DateTimeFormatter instead of SimpleDateFormat. Simple Beautiful Strong immutable thread-safe.
The variable conversion mode is as follows: Refer to [1] [2]
/ * Instant * /
Instant dateInstant = date.toInstant();
/ / Timestamp Instant
Instant timestampInstant = timestamp.toInstant();
/ / the 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());
/ / turn LocalDateTime Timestamp
LocalDateTime timestampLocalDateTime = timestamp.toLocalDateTime();
/ / turn LocalDateTime Calendar
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();
/ / turn LocalDate Timestamp
LocalDate timestampLocalDate = timestamp.toLocalDateTime().toLocalDate();
/ / turn LocalDate Calendar
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();
Copy the code
[1] JSR310 Time type conversion
[2] Java 8 Time and date API 20 cases