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实例,有效地防止了多个线程之间的干扰。