1.簡單使用
public class SimpleUse {
public static void main(String[] args) throws ParseException {
//創建SimpleDateFormat對象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//常見用法一:格式化日期爲想要的字符串
String dateStr = sdf.format(new Date(System.currentTimeMillis()));
System.out.println(dateStr);
//常見用法二:解析字符串爲日期
Date date = sdf.parse("2015-06-29 22:12:13");
System.out.println(date);
}
}
可以得到如下的運行結果:
<span style="font-size:14px;">2015-06-29 22:12:57
Mon Jun 29 22:12:13 CST 2015</span>
2.性能消耗
創建SimpleDateFormat是非常消耗性能的。
public class Problem1 {
public static void main(String[] args) throws ParseException {
String dateStr = "2015-06-29 12:22:22";
String pattern = "yyyy-MM-dd HH:mm:ss";
test1(dateStr,pattern);
test2(dateStr,pattern);
}
public static void test1(String dateStr,String pattern) throws ParseException {
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
sdf.parse(dateStr);
}
long end = System.currentTimeMillis();
System.out.println("cost1=" + (end - start));
}
public static void test2(String dateStr,String pattern) throws ParseException {
Map<String, SimpleDateFormat> map = new HashMap<String, SimpleDateFormat>();
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
if (!map.containsKey(pattern)) {
map.put(pattern, new SimpleDateFormat(pattern));
}
SimpleDateFormat sdf = map.get(pattern);
sdf.parse(dateStr);
}
long end = System.currentTimeMillis();
System.out.println("cost2=" + (end - start));
}
}
運行得到如下結果:
cost1=640
cost2=62
如上所示,代碼中循環了10000次的反覆調用,最後的時間消耗還是比較明顯的。
可能你會覺得創建對象本身就會消耗性能,我先開始也是有這個疑問,看下面這段代碼也許會比較直觀。
public class Problem2 {
public static void main(String[] args) throws ParseException {
String dateStr = "2015-06-29 12:22:22";
String pattern = "yyyy-MM-dd HH:mm:ss";
test1(dateStr, pattern);
test2(dateStr, pattern);
}
public static void test1(String dateStr, String pattern) throws ParseException {
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
SomeObj sdf = new SomeObj(pattern);
sdf.parse(dateStr);
}
long end = System.currentTimeMillis();
System.out.println("cost1=" + (end - start));
}
public static void test2(String dateStr, String pattern) throws ParseException {
Map<String, SomeObj> map = new HashMap<String, SomeObj>();
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
if (!map.containsKey(pattern)) {
map.put(pattern, new SomeObj(pattern));
}
SomeObj sdf = map.get(pattern);
sdf.parse(dateStr);
}
long end = System.currentTimeMillis();
System.out.println("cost2=" + (end - start));
}
}
運行得到如下結果:
cost1=2
cost2=10
這裏的SomeObj只是一個比較的對象而已,不用去管裏面是怎麼寫的。
比較這兩段代碼可知:
SimpleDateFormat的創建是很消耗性能的。
解決辦法:
private static Map<String, SimpleDateFormat> map = new HashMap<String, SimpleDateFormat>();
public static SimpleDateFormat getInstance(String pattern) {
if (!map.containsKey(pattern)) {
map.put(pattern, new SimpleDateFormat(pattern));
}
return map.get(pattern);
}
如上代碼段所示,使用單例模式的確會提升性能,但這樣做還是不夠的。
3.安全性
SimepleDateFormat是非線程安全的。具體解釋我不敢肯定。應該是在調用parse、format的時候會使用到一個Calendar對象。每次使用的時候,calendar會先後調用clean、getTime方法。這樣如果多線程公用同一個calendar是會出問題的。(具體源代碼日後再分析補充,如果描述不正確還請指出)。
具體的效果我們看下代碼。
public class MultipleThreadDemo implements Runnable {
String pattern;
public MultipleThreadDemo(String pattern) {
this.pattern = pattern;
}
@Override
public void run() {
SimpleDateFormat sdf = getInstance();
int count = 10;
while (count-- > 0) {
try {
sdf.parse(pattern);
sdf.format(new Date(System.currentTimeMillis()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MultipleThreadDemo d1 = new MultipleThreadDemo("2015-06-21");
MultipleThreadDemo d2 = new MultipleThreadDemo("2015-06-22");
new Thread(d1).start();
new Thread(d2).start();
}
private static SimpleDateFormat sdf_ = new SimpleDateFormat("yyyy-MM-dd");
public static SimpleDateFormat getInstance() {
return sdf_;
}
}
運行得到如下結果:
java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1110)
at java.lang.Double.parseDouble(Double.java:540)
at java.text.DigitList.getDouble(DigitList.java:168)
at java.text.DecimalFormat.parse(DecimalFormat.java:1321)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1793)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1455)
at java.text.DateFormat.parse(DateFormat.java:355)
at com.cp.demo.SimpleDateFormatDemo.MultipleThreadDemo.run(MultipleThreadDemo.java:20)
at java.lang.Thread.run(Thread.java:745)
這樣的話,多線程共同使用同一個SimpleDateFormat是不行的。
4.最終解決辦法
public class SolveDemo implements Runnable {
String pattern;
public SolveDemo(String pattern) {
this.pattern = pattern;
}
@Override
public void run() {
SimpleDateFormat sdf = getInstance();
int count = 100;
while(count-->0){
try {
sdf.parse(pattern);
sdf.format(new Date(System.currentTimeMillis()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SolveDemo d1 = new SolveDemo("2015-06-21");
SolveDemo d2 = new SolveDemo("2015-06-22");
new Thread(d1).start();
new Thread(d2).start();
}
private static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>();
public static SimpleDateFormat getInstance() {
SimpleDateFormat sdf = SolveDemo.threadLocal.get();
if (sdf != null) {
return sdf;
}
SimpleDateFormat sdf_ = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(Thread.currentThread().getName()+":創建一個sdf");
SolveDemo.threadLocal.set(sdf_);
return sdf_;
}
}
運行得到如下結果:
Thread-0:創建一個sdf
Thread-1:創建一個sdf
這裏使用了ThreadLocal爲每個線程創建了單獨的一個SimpleDateFormat實例,有效地防止了多個線程之間的干擾。