Java的面向?qū)ο缶幊?/a>">Java的面向?qū)ο缶幊?/a>
803
2025-04-02
目錄
一、場景
二、SimpledateFormat線程為什么是線程不安全的呢?
驗(yàn)證SimpledateFormat線程不安全
三、FastDateFormat源碼分析
實(shí)踐
四、結(jié)論
一、場景
在Java8以前,要格式化日期時(shí)間,就需要用到SimpleDateFormat。
但我們知道SimpleDateFormat是線程不安全的,處理時(shí)要特別小心,要加鎖或者不能定義為static,要在方法內(nèi)new出對象,再進(jìn)行格式化。很麻煩,而且重復(fù)地new出對象,也加大了內(nèi)存開銷。
后來Apache 在commons-lang 包中擴(kuò)展了FastDateFormat對象,它是一個(gè)線程安全的,可以用來完美替換SimpleDateFormat。
二、SimpleDateFormat線程為什么是線程不安全的呢?
來看看SimpleDateFormat的源碼
// Called from Format after creating a FieldDelegate private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) { // Convert input date to time field list calendar.setTime(date); ... }
1
2
3
4
5
6
7
問題就出在成員變量calendar,如果在使用SimpleDateFormat時(shí),用static定義,那SimpleDateFormat變成了共享變量。那SimpleDateFormat中的calendar就可以被多個(gè)線程訪問到。
驗(yàn)證SimpleDateFormat線程不安全
public class SimpleDateFormatDemoTest { private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) { //1、創(chuàng)建線程池 ExecutorService pool = Executors.newFixedThreadPool(5); //2、為線程池分配任務(wù) ThreadPoolTest threadPoolTest = new ThreadPoolTest(); for (int i = 0; i < 10; i++) { pool.submit(threadPoolTest); } //3、關(guān)閉線程池 pool.shutdown(); } static class ThreadPoolTest implements Runnable{ private volatile int i=0; @Override public void run() { while (i<10){ String dateString = simpleDateFormat.format(new Date()); try { Date parseDate = simpleDateFormat.parse(dateString); String dateString2 = simpleDateFormat.format(parseDate); System.out.println(Thread.currentThread().getName()+" : "+i++); System.out.println(dateString.equals(dateString2)); System.out.println("-------------------------"); } catch (ParseException e) { e.printStackTrace(); } } } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
出現(xiàn)了兩次false,說明線程是不安全的。
三、FastDateFormat源碼分析
Apache Commons Lang 3.5
1
//FastDateFormat @Override public String format(final Date date) { return printer.format(date); } @Override public String format(final Date date) { final Calendar c = Calendar.getInstance(timeZone, locale); c.setTime(date); return applyRulesToString(c); }
1
2
3
4
5
6
7
8
9
10
11
12
源碼中 Calender 是在 format 方法里創(chuàng)建的,肯定不會出現(xiàn) setTime 的線程安全問題。這樣線程安全疑惑解決了。那還有性能問題要考慮?
我們來看下FastDateFormat是怎么獲取的
FastDateFormat.getInstance(); FastDateFormat.getInstance(CHINESE_DATE_TIME_PATTERN);
1
2
看下對應(yīng)的源碼
/** * 獲得 FastDateFormat實(shí)例,使用默認(rèn)格式和地區(qū) * * @return FastDateFormat */ public static FastDateFormat getInstance() { return CACHE.getInstance(); } /** * 獲得 FastDateFormat 實(shí)例,使用默認(rèn)地區(qū)
* 支持緩存 * * @param pattern 使用{@link Java.text.SimpleDateFormat} 相同的日期格式 * @return FastDateFormat * @throws IllegalArgumentException 日期格式問題 */ public static FastDateFormat getInstance(final String pattern) { return CACHE.getInstance(pattern, null, null); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
這里有用到一個(gè)CACHE,看來用了緩存,往下看
private static final FormatCache
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在getInstance 方法中加了ConcurrentMap 做緩存,提高了性能。且我們知道ConcurrentMap 也是線程安全的。
實(shí)踐
/** * 年月格式 {@link FastDateFormat}:yyyy-MM */ public static final FastDateFormat NORM_MONTH_FORMAT = FastDateFormat.getInstance(NORM_MONTH_PATTERN);
1
2
3
4
//FastDateFormatpublic static FastDateFormat getInstance(final String pattern) { return CACHE.getInstance(pattern, null, null);}
1
如圖可證,是使用了ConcurrentMap 做緩存。且key值是格式,時(shí)區(qū)和locale(語境)三者都相同為相同的key。
四、結(jié)論
java8之前,可使用FastDateFormat 替換SimpleDateFormat,達(dá)到線程安全的目的;
java8及以上的,java8提供了一套新的日期時(shí)間API,可以使用DateTimeFormatter來代替SimpleDateFormat。具體的源碼分析,可以看這里,傳送門:萬字博文教你搞懂java源碼的日期和時(shí)間相關(guān)用法
Java 任務(wù)調(diào)度
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。