天天看點

ThreadLocal

作用

ThreadLocal 是感啥的,有啥作用?怎麼實作的?被問到了非常的奇怪!平時感覺使用的場景在通路的時候儲存HTTPRequest,友善Service、Dao層通路變量,也就是人手一支筆的問題,通路大家去通路,有點浪費記憶體。

怎麼實作的?

我是這麼回到的,每個線程都有一個變量的對象,然後放置在ThreadLocal 的Map中進行緩存起來,友善目前線程去擷取這個變量的值。問題來了?Map的key是什麼?很理所當然的回到,擷取目前的線程,然後讓目前線程作為key,vulue變量的值為目前Map.Entry 的一項,感覺這樣的回到OK的,查找網頁+源碼發現不對!和我們的想法有點偏離了,這樣的一個公共的Map,線程銷毀的時候都要自己必須的去調用這個邏輯,而且當線程的數量多啦,通路效率是不是也不能n(1)?帶着這個問題了一些回答:

正确了解ThreadLocal

Java并發程式設計:深入剖析ThreadLocal

首先,ThreadLocal 不是用來解決共享對象的多線程通路問題的,一般情況下,通過ThreadLocal.set() 到線程中的對象是該線程自己使用的對象,其他線程是不需要通路的,也通路不到的。各個線程中通路的是不同的對象。

另外,說ThreadLocal使得各線程能夠保持各自獨立的一個對象,并不是通過ThreadLocal.set()來實作的,而是通過每個線程中的new 對象 的操作來建立的對象,每個線程建立一個,不是什麼對象的拷貝或副本。

下面來看一個hibernate中典型的ThreadLocal的應用:

private static final ThreadLocal threadSession = new ThreadLocal();  

public static Session getSession() throws InfrastructureException {  
    Session s = (Session) threadSession.get();  
    try {  
        if (s == null) {  
            s = getSessionFactory().openSession();  
            threadSession.set(s);  
        }  
    } catch (HibernateException ex) {  
        throw new InfrastructureException(ex);  
    }  
    return s;  
}             

可以看到在getSession()方法中,首先判斷目前線程中有沒有放進去session,如果還沒有,那麼通過sessionFactory().openSession()來建立一個session,再将session set到線程中,實際是放到目前線程的ThreadLocalMap這個map中,這時,對于這個session的唯一引用就是目前線程中的那個ThreadLocalMap,而threadSession作為這個值的key,要取得這個session可以通過threadSession.get()來得到,裡面執行的操作實際是先取得目前線程中的ThreadLocalMap,然後将threadSession作為key将對應的值取出。這個session相當于線程的私有變量,而不是public的。

顯然,其他線程中是取不到這個session的,他們也隻能取到自己的ThreadLocalMap中的東西。要是session是多個線程共享使用的,那還不亂套了。

試想如果不用ThreadLocal怎麼來實作呢?可能就要在action中建立session,然後把session一個個傳到service和dao中,這可夠麻煩的。或者可以自己定義一個靜态的map,将目前thread作為key,建立的session作為值,put到map中,應該也行,這也是一般人的想法 這裡說明了問題,我就是一般的人哈哈,這樣減少了很多的煩心事,第一個就是不用維護一個非常大的Map很多的線程維護起來這個Map中的資料量非常的大第二個就是目前的Map裡面的資料必須要線程手動的去銷毀和線程的生命周期沒有關系。

但事實上,ThreadLocal的實作剛好相反,它是在每個線程中有一個map,而将ThreadLocal執行個體作為key,這樣每個map中的項數很少,而且當線程銷毀時相應的東西也一起銷毀了,不知道除了這些還有什麼其他的好處。我也覺得這個就是ThreadLocal這樣實作的原因吧。

總之,ThreadLocal不是用來解決對象共享通路問題的,而主要是提供了保持對象的方法和避免參數傳遞的友善的對象通路方式。歸納了兩點:

1。每個線程中都有一個自己的ThreadLocalMap類對象,可以将線程自己的對象保持到其中,各管各的,線程可以正确的通路到自己的對象。

2。将一個共用的ThreadLocal靜态執行個體作為key,将不同對象的引用儲存到不同線程的ThreadLocalMap中,然後線上程執行的各處通過這個靜态ThreadLocal執行個體的get()方法取得自己線程儲存的那個對象,避免了将這個對象作為參數傳遞的麻煩。

實作原理

/**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

    /**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }


    /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }


    /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
           
/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

主動銷毀

    /**
     * This method is called by the system to give a Thread
     * a chance to clean up before it actually exits.
     */
    private void exit() {
        if (group != null) {
            group.threadTerminated(this);
            group = null;
        }
        /* Aggressively null out all reference fields: see bug 4006245 */
        target = null;
        /* Speed the release of some of these resources */
        threadLocals = null;
        inheritableThreadLocals = null;
        inheritedAccessControlContext = null;
        blocker = null;
        uncaughtExceptionHandler = null;
    }           

總結

下一篇: ThreadLocal 1