小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

java的SimpleDateFormat線程不安全出問題了,項目不支持java8,虛竹教你這一招

 小虛竹 2021-11-30

目錄

一、場景

在java8以前,要格式化日期時間,就需要用到SimpleDateFormat。

但我們知道SimpleDateFormat是線程不安全的,處理時要特別小心,要加鎖或者不能定義為static,要在方法內(nèi)new出對象,再進行格式化。很麻煩,而且重復(fù)地new出對象,也加大了內(nèi)存開銷。

后來Apache 在commons-lang 包中擴展了FastDateFormat對象,它是一個線程安全的,可以用來完美替換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);
...
    }

問題就出在成員變量calendar,如果在使用SimpleDateFormat時,用static定義,那SimpleDateFormat變成了共享變量。那SimpleDateFormat中的calendar就可以被多個線程訪問到。

驗證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();
}
            }
        }
    }
}

image-2021071474807474

出現(xiàn)了兩次false,說明線程是不安全的。

三、FastDateFormat源碼分析

 Apache Commons Lang 3.5
//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);
}

源碼中 Calender 是在 format 方法里創(chuàng)建的,肯定不會出現(xiàn) setTime 的線程安全問題。這樣線程安全疑惑解決了。那還有性能問題要考慮?

我們來看下FastDateFormat是怎么獲取的

FastDateFormat.getInstance();
FastDateFormat.getInstance(CHINESE_DATE_TIME_PATTERN);

看下對應(yīng)的源碼

/**
 * 獲得 FastDateFormat實例,使用默認格式和地區(qū)
 *
 * @return FastDateFormat
 */
public static FastDateFormat getInstance() {
   return CACHE.getInstance();
}

/**
 * 獲得 FastDateFormat 實例,使用默認地區(qū)<br>
 * 支持緩存
 *
 * @param pattern 使用{@link java.text.SimpleDateFormat} 相同的日期格式
 * @return FastDateFormat
 * @throws IllegalArgumentException 日期格式問題
 */
public static FastDateFormat getInstance(final String pattern) {
   return CACHE.getInstance(pattern, null, null);
}

這里有用到一個CACHE,看來用了緩存,往下看

private static final FormatCache<FastDateFormat> CACHE = new FormatCache<FastDateFormat>(){
   @Override
   protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
      return new FastDateFormat(pattern, timeZone, locale);
   }
};

//
abstract class FormatCache<F extends Format> {
    ...
    private final ConcurrentMap<Tuple, F> cInstanceCache = new ConcurrentHashMap<>(7);

private static final ConcurrentMap<Tuple, String> C_DATE_TIME_INSTANCE_CACHE = new ConcurrentHashMap<>(7);
    ...
}

image-20210728914309

在getInstance 方法中加了ConcurrentMap 做緩存,提高了性能。且我們知道ConcurrentMap 也是線程安全的。

實踐

/**
 * 年月格式 {@link FastDateFormat}:yyyy-MM
 */
public static final FastDateFormat NORM_MONTH_FORMAT = FastDateFormat.getInstance(NORM_MONTH_PATTERN);

image-2021072895013629

//FastDateFormatpublic static FastDateFormat getInstance(final String pattern) {   return CACHE.getInstance(pattern, null, null);}

image-20210728205104833

image-2021072895259113

如圖可證,是使用了ConcurrentMap 做緩存。且key值是格式,時區(qū)和locale(語境)三者都相同為相同的key。

四、結(jié)論

java8之前,可使用FastDateFormat 替換SimpleDateFormat,達到線程安全的目的;

java8及以上的,java8提供了一套新的日期時間API,可以使用DateTimeFormatter來代替SimpleDateFormat。具體的源碼分析,可以看這里,傳送門:萬字博文教你搞懂java源碼的日期和時間相關(guān)用法

    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多