【Java並發編程】12、ThreadLocal 解決SimpleDateFormat非線程安全


大致意思:Tim Cull碰到一個SimpleDateFormat帶來的嚴重的性能問題,該問題主要有SimpleDateFormat引發,創建一個 SimpleDateFormat實例的開銷比較昂貴,解析字符串時間時頻繁創建生命周期短暫的實例導致性能低下。即使將 SimpleDateFormat定義為靜態類變量,貌似能解決這個問題,但是SimpleDateFormat是非線程安全的,同樣存在問題,如果用 ‘synchronized’線程同步同樣面臨問題,同步導致性能下降(線程之間序列化的獲取SimpleDateFormat實例)。

早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal為解決多線程程序的並發問題提供了一種新的思路。使用這個工具類可以很簡潔地編寫出優美的多線程程序。

ThreadLocal很容易讓人望文生義,想當然地認為是一個“本地線程”。其實,ThreadLocal並不是一個Thread,而是Thread的局部變量,也許把它命名為ThreadLocalVariable更容易讓人理解一些。

當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。

從線程的角度看,目標變量就象是線程的本地變量,這也是類名中“Local”所要表達的意思。

Tim Cull使用Threadlocal解決了此問題,對於每個線程SimpleDateFormat不存在影響他們之間協作的狀態,為每個線程創建一個SimpleDateFormat變量的拷貝或者叫做副本,代碼如下:

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/** 
 * 使用ThreadLocal以空間換時間解決SimpleDateFormat線程安全問題。 
 */
public class DateUtil {
    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    @SuppressWarnings("rawtypes")
    private static ThreadLocal threadLocal = new ThreadLocal() {
        protected synchronized Object initialValue() {
            return new SimpleDateFormat(DATE_FORMAT);
        }
    };

    public static DateFormat getDateFormat() {
        return (DateFormat) threadLocal.get();
    }

    public static Date parse(String textDate) throws ParseException {
        return getDateFormat().parse(textDate);
    }
}

創建一個ThreadLocal類變量,這里創建時用了一個匿名類,覆蓋了initialValue方法,主要作用是創建時初始化實例。也可以采用下面方式創建;

import java.text.DateFormat;
import java.text.SimpleDateFormat;

/** 
 * 使用ThreadLocal以空間換時間解決SimpleDateFormat線程安全問題
 */
public class DateUtil {    
    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    private static ThreadLocal threadLocal = new ThreadLocal();
    // 第一次調用get將返回null
    // 獲取線程的變量副本,如果不覆蓋initialValue,第一次get返回null,
    // 故需要初始化一個SimpleDateFormat,並set到threadLocal中
    public static DateFormat getDateFormat() {
        DateFormat df = (DateFormat) threadLocal.get();
        if (df == null) {
            df = new SimpleDateFormat(DATE_FORMAT);  
            threadLocal.set(df);
        }
        return df;
    }
}

 

當然也可以使用:

apache commons-lang包的DateFormatUtils或者FastDateFormat實現,apache保證是線程安全的,並且更高效


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM