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