In normal work, we often use the simpledateformat class for date conversion, but note that it is unsafe under multithreading.
simpleDateFormat = new SimpleDateFormat(“yyyy-MM-dd”);
###Unsafe code test
package cn.thread.first.unsafe;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
class SimpleDome extends Thread {
private SimpleDateFormat simpleDateFormat;
private String str;
public SimpleDome(SimpleDateFormat sf, String str) {
this.simpleDateFormat = sf;
this.str = str;
}
@Override
public void run() {
try {
Date date = simpleDateFormat.parse(str);
System.out.println(str + "=" + date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
public class SimpleDateFormatDome {
public static void main(String args[]) {
//First: the output results are not matched
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String[] strs = {"2017-01-10", "2017-01-11", "2017-01-12", "2017-01-13", "2017-01-14", "2017-01-15"};
SimpleDome[] domes = new SimpleDome[6];
for (int i = 0; i < 6; i++) {
domes[i] = new SimpleDome(simpleDateFormat, strs[i]);
}
for (int i = 0; i < 6; i++) {
domes[i].start();
}
}
//Second: report various exceptions
final SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM-dd");
new Thread(new Runnable() {
public void run() {
String str = "2017-01-10";
Date date = new SimpleDome(simpleDateFormat2, str).stToDate();
System.out.println(str + "=" + date);
}
}).start();
new Thread(new Runnable() {
public void run() {
String str = "2017-01-11";
Date date = new SimpleDome(simpleDateFormat2,str).stToDate();
System.out.println(str + "=" + date);
}
}).start();
}
####First output result
Exception 1: exception in thread "thread-3" java.lang.numberformatexception: for input string: "20172017e4"
Exception 2: exception in thread "thread-2" java.lang.numberformatexception: multiple points
Exception 3: sometimes the converted date can be aligned, and sometimes it can't be aligned.
####Second: report various exceptions
Exception 1: exception in thread "thread-3" java.lang.numberformatexception: for input string: "20172017e4"
Exception 2: exception in thread "thread-2" java.lang.numberformatexception: multiple points
Exception 3: exception in thread "thread-3" java.lang.numberformatexception: for input string: ""
###Solution I
- Add lock during conversion:
private static final Object object=newe Object();
@Override
public void run() {
synchronized(object){
try {
Date date = simpleDateFormat.parse(str);
System.out.println(str + "=" + date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
Under high concurrency, this method will lead to a large number of thread blocking and seriously affect the performance. It is generally not recommended.
###Solution II
class SimpleDome
...
private SimpleDateFormat simpleDateFormat= new SimpleDateFormat("yyyy-MM-dd");
For each thread, it is a new simpledateformat object, and there is no resource competition. The disadvantage of this method is that when the traffic is large, a large number of simpledateformat objects will be created and discarded after conversion, which will occupy memory for a short time, resulting in waste collection. Time consuming, space consuming and even less cost-effective.
###Solution 3 (recommended)
package cn.thread.first.unsafe;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
class SimpleHelp {
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
//Writing method 1, together with the initialization above!
public static Date convert(String source) {
try {
Thread.sleep(100);
return df.get().parse(source);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//Writing method 2
// public static DateFormat getDateFormat() {
// DateFormat dateFormat = df.get();
// if (dateFormat == null) {
// dateFormat = new SimpleDateFormat("yyyy-MM-dd");
// df.set(dateFormat);
// }
// return dateFormat;
// }
//
// public static Date convert2(String source) {
// try {
// Thread.sleep(100);
// return getDateFormat().parse(source);
// } catch (Exception e) {
// e.printStackTrace();
// }
// return null;
// }
}
public class SimpleDateFormatDome {
public static void main(String args[]) {
//The following is thread safe
new Thread(new Runnable() {
public void run() {
String str = "2017-01-12";
Date date = SimpleHelp.convert(str);
System.out.println(str + "=" + date);
}
}).start();
new Thread(new Runnable() {
public void run() {
String str = "2017-01-13";
Date date = SimpleHelp.convert(str);
System.out.println(str + "=" + date);
}
}).start();
new Thread(new Runnable() {
public void run() {
String str = "2017-01-14";
Date date = SimpleHelp.convert(str);
System.out.println(str + "=" + date);
}
}).start();
}
}
Normal output results.
Note: using ThreadLocal will save a copy for each thread. Each thread has its own ThreadLocal - > dateformat, which means that each thread has its own dateformat.
###Why is simpledateformat unsafe?
Simpledateformat inherits the dateformat. In the dateformat, an object of the calendar class with a protected attribute is defined: calendar. Just because the concept of calendar is complex and involves time zone and localization, the implementation of JDK uses member variables to pass parameters, which causes errors in multithreading.
###Summary
-
Convert or format dates in Java using local dateformat or simpledateformat objects. Localize them to ensure that they are not shared across multiple threads.
-
If you share a date for the simpledateformat class in Java, you need to synchronously call the format () and parse () methods externally, because they will change the state of the dateformat object and can create subtleties and difficulties to fix errors when formatting strings or creating dates in Java. It is best to avoid sharing the dateformat class.
-
If you have options, use the joda date library for date and time related operations. It uses the Java date API, which is easy to understand and portable, and solves all thread safety problems related to simpledateformat in Java.
-
Another good alternative to simpledateformat in Java is the commons.lang package of Apaches, which contains a utility class named fastdateformat and a thread safe alternative to simpledateformat in Java.
-
Another way to synchronize dateformat and simpledateformat is to use ThreadLocal, which creates simpledateformat based on each thread, but if not used carefully, it may be the source of serious memory leakage and java.lang.outofmemoryerror, so avoid that you have no other choice.
-
The insecurity of simpledateformat comes from the use of a global variable calendar, which performs clear and set operations during the operation, similar to - 1 and + 1 operations. This leads to the unsafe operation of simpledateformat under multithreading.