合理使用SimpleDateFormat

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值