1. 程式人生 > >ThreadLocal解決SimpleDateFormat多線程安全問題中遇到的困惑

ThreadLocal解決SimpleDateFormat多線程安全問題中遇到的困惑

檢查 initial hashcode n) lan ext 線程安全 args []

測試代碼:

public class Main {

    public static void main(String[] args) {

        for (int k = 0; k < 10; k++) {

            Runnable target = new Runnable() {

                @Override
                public void run() {
                    Object obj = dateFormatter.get();
                    System.out.println(Thread.currentThread().getName() + "  =>  " + obj);
                     obj = dateFormatter.get();
                }
            };
            new Thread(target, k+"").start();

        }

    }

    private static final ThreadLocal<SimpleDateFormat> dateFormatter = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd");
        }
    };
}

輸出結果:

8 => java.text.SimpleDateFormat@f67a0200
5 => java.text.SimpleDateFormat@f67a0200
6 => java.text.SimpleDateFormat@f67a0200
...
7 => java.text.SimpleDateFormat@f67a0200

咦?怎麽全是f67a0200一個實例?跟蹤ThreadLocal代碼尋找原因,百思不得其解。最後突然懷疑是SimpleDateFormat中toString方法的問題,SimpleDateFormat#toString源碼如下:

    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

SimpleDateFormat#hashCode代碼如下,其中pattern變量是SimpleDateFormat的格式字符串值( public SimpleDateFormat(String pattern))。

    @Override
    public int hashCode()
    {
        return pattern.hashCode();
        // just enough fields for a reasonable distribution
    }

 

所以如上原因可以得知,由於自己實現的initialValue方法的SimpleDateFormat的pattern都一樣,所以不同sdf實例的toString最終輸出相同。

     protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd");
        }

  

產生困惑原因:

  通常子類在沒有重寫toString方法時,我們都可以簡單的根據toString值進行判斷是否是一個實例,但是由於SimpleDateFormat自己實現了toString所以這個規則不在生效。

提醒:

  以後盡可能不要簡單的將toString輸出用來判斷是否是一個實例,如果需要這麽判斷的話一定要檢查toString方法。

拓展:

  在java.lang.ThreadLocal#getMap方法中可以發現原來java的Thread對線程局部變量自身就有支持,在Thread中有一個ThreadLocalMap的成員變量。java.lang.ThreadLocal#getMap源碼如下:

    ThreadLocalMap getMap(Thread t) {
     //threadLocals是一個默認修飾符成員變量 return t.threadLocals; }

  

ThreadLocal解決SimpleDateFormat多線程安全問題中遇到的困惑