在企業應用開發中,經常會遇到日期的相關處理,說實話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>