在企业应用开发中,经常会遇到日期的相关处理,说实话jdk自带的日期方法很难用。就我个人而言我一般都会采用joda-time来替代jdk自身的日期。
这篇文章是杂记,所以写的比较零散,希望大家不要见怪。
先来说说jdk自带的simpledateformat类吧。simpledateformat 是 java 中一个非常常用的类用来对日期字符串进行解析和格式化输出,但如果使用不小心会导致非常微妙和难以调试的问题,因为 dateformat 和 simpledateformat 类不都是线程安全的,在多线程环境下调用 format() 和 parse() 方法应该使用同步代码来避免问题。
下面是你在使用 simpledateformat 应该要小心的几点:
确保不会在多线程状态下使用同一个 dateformat 或者 simpledateformat 实例
如果多线程情况下需要访问同一个实例,那么请用同步方法
你也可以使用 commons-lang 包中的 fastdateformat 工具类
另外你也可以使用 threadlocal 来处理这个问题
下面我们通过代码来说明上面的问题:
以下的代码为我们展示了如何在一个线程环境里面使用dateformat把字符串日期转换为日期对象。创建一个实例来获取日期格式会比较高效,因为系统不需要多次获取本地语言和国家。
1
2
3
4
5
6
7
8
9
10
11
<code>public</code> <code>class</code> <code>dateformattest {</code>
<code> </code>
<code> </code><code>private</code> <code>final</code> <code>dateformat format =</code>
<code> </code><code>new</code> <code>simpledateformat(</code><code>"yyyymmdd"</code><code>);</code>
<code> </code><code>public</code> <code>date convert(string source)</code>
<code> </code><code>throws</code> <code>parseexception{</code>
<code> </code><code>date d = format.parse(source);</code>
<code> </code><code>return</code> <code>d;</code>
<code> </code><code>}</code>
<code>}</code>
这段代码是非线程安全的。我们可以通过在多个线程中调用它。在以下调用的代码中,我创建了一个有两个线程的线程池,并提交了5个日期转换任务,之后查看运行结果:
12
13
14
15
16
17
18
19
20
21
22
<code>final</code> <code>dateformattest t =</code><code>new</code> <code>dateformattest();</code>
<code>callable<date> task =</code><code>new</code> <code>callable<date>(){</code>
<code> </code><code>public</code> <code>date call()</code><code>throws</code> <code>exception {</code>
<code> </code><code>return</code> <code>t.convert(</code><code>"20100811"</code><code>);</code>
<code> </code><code>}</code>
<code>};</code>
<code>//让我们尝试2个线程的情况</code>
<code>executorservice exec = executors.newfixedthreadpool(</code><code>2</code><code>);</code>
<code>list<future<date>> results =</code>
<code> </code><code>new</code> <code>arraylist<future<date>>();</code>
<code>//实现5次日期转换</code>
<code>for</code><code>(</code><code>int</code> <code>i =</code><code>0</code><code>; i <</code><code>5</code><code>; i++){</code>
<code> </code><code>results.add(exec.submit(task));</code>
<code>exec.shutdown();</code>
<code>//查看结果</code>
<code>for</code><code>(future<date> result : results){</code>
<code> </code><code>system.out.println(result.get());</code>
代码的运行结果并非如我们所愿 - 有时候,它输出正确的日期,有时候会输出错误的(例如.sat jul 31 00:00:00 bst 2012),有些时候甚至会抛出numberformatexception!
如何并发使用dateformat类
我们可以有多种方法在线程安全的情况下使用dateformat类。
1. 同步
最简单的方法就是在做日期转换之前,为dateformat对象加锁。这种方法使得一次只能让一个线程访问dateformat对象,而其他线程只能等待。
<code>public</code> <code>date convert(string source)</code>
<code> </code><code>throws</code> <code>parseexception{</code>
<code> </code><code>synchronized</code><code>(format) {</code>
2. 使用threadlocal
另外一个方法就是使用threadlocal变量去容纳dateformat对象,也就是说每个线程都有一个属于自己的副本,并无需等待其他线程去释放它。这种方法会比使用同步块更高效。
<code> </code><code>private</code> <code>static</code> <code>final</code> <code>threadlocal<dateformat> df</code>
<code> </code><code>= </code><code>new</code> <code>threadlocal<dateformat>(){</code>
<code> </code><code>@override</code>
<code> </code><code>protected</code> <code>dateformat initialvalue() {</code>
<code> </code><code>return</code> <code>new</code> <code>simpledateformat(</code><code>"yyyymmdd"</code><code>);</code>
<code> </code><code>};</code>
<code> </code><code>throws</code> <code>parseexception{</code>
<code> </code><code>date d = df.get().parse(source);</code>
3. joda-time
<code>import</code> <code>org.joda.time.datetime;</code>
<code>import</code> <code>org.joda.time.format.datetimeformat;</code>
<code>import</code> <code>org.joda.time.format.datetimeformatter;</code>
<code>import</code> <code>java.util.date;</code>
<code> </code><code>private</code> <code>final</code> <code>datetimeformatter fmt =</code>
<code> </code><code>datetimeformat.forpattern(</code><code>"yyyymmdd"</code><code>);</code>
<code> </code><code>public</code> <code>date convert(string source){</code>
<code> </code><code>datetime d = fmt.parsedatetime(source);</code>
<code> </code><code>returnd.todate();</code>