您的位置:首页 > 其它

合理使用SimpleDateFormat

2015-06-29 22:16 507 查看
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实例,有效地防止了多个线程之间的干扰。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息