<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    swzhebei

    常用鏈接

    統(tǒng)計

    最新評論

    • 1.?re: 調用百度地圖小實例
    • 如果我有100個經緯度 請問,您是不是再代碼里寫100個?你這樣沒有價值,如何獲取動態(tài)的請說明!
    • --toly
    • 2.?re: 調用百度地圖小實例
    • 更改經緯度就不行了!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    • --你姥姥

    ThreadLocal類(1)

    1.目的

    ThreadLocal目的是保存一些線程級別的全局變量,比如connection,或者事務上下文,避免這些值需要一直通過函數參數的方式一路傳遞。

    2. 常見用法
    舉例其中一種常見用法:

    public class Test2 {
    	public static void main(String[] args) throws InterruptedException {
    		testThreadLocal();
    	}
     
    	private static void testThreadLocal() {
    		Util.setGlobalName("zili.dengzl");
    		new Foo().printName();
    	}
    }
     
    class Foo{
    	public void printName(){
    		System.out.println("globalName="+Util.getGlobalName());
    	}
    }
     
    class Util {
    	private static final ThreadLocal<String> globalName = new ThreadLocal<String>();
     
    	public static String getGlobalName() {
    		return globalName.get();
    	}
     
    	public static void setGlobalName(String name) {
    		globalName.set(name);
    	}
    }

    3.實現(xiàn)分析

    要實現(xiàn)上面這樣的功能,最簡單的想法是用一個Map<Thread,T>,如下:

    class MockThreadLocal<T> {
    	private Map<Thread, T> map = new HashMap<Thread, T>();
     
    	public T get() {
    		return (T) map.get(Thread.currentThread());
    	}
     
    	public void set(T value) {
    		map.put(Thread.currentThread(), value);
    	}
    }

    這樣也能實現(xiàn)ThreadLocal的效果,但是有一個問題,當對應的線程消失后,map中對應的線程值并不會被回收,從而造成內存泄露。

    事實上ThreadLocal是這樣做的:

    每個Thread都有一個threadLocalMap,key是threadLocal對象,value是具體使用的值。ThreadLocal對象的get就是先取得當前的Thread,然后從這個Thread的threadLcoalMap中取出值。set類似。

    下面看下具體代碼:

        /**
         * 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)
                    return (T)e.value;
            }
            return setInitialValue();
        }

    注意這里如果取到沒有該線程對應的值,會調用setInitialValue();,最終調用initialValue()生成一個值,這也是我們很多場景下要override這個方法的原因;

     

    下面看一下getMap(Thread t)方法:

        ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }

    在Thread類中:

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

    由此可見,所有的ThreadLocal的信息,最終是關聯(lián)到Thread上的,線程消失后,對應的Thread對象也被回收,這時對應的ThreadLocal對象(該線程部分)也會被回收。

    這里為什么是一個ThreadLocalMap呢,因為一個線程可以有多個ThreadLocal變量,通過map.getEntry(this)取得對應的某個具體的變量。

            private Entry getEntry(ThreadLocal key) {
                int i = key.threadLocalHashCode & (table.length - 1);
                Entry e = table[i];
                if (e != null && e.get() == key)
                    return e;
                else
                    return getEntryAfterMiss(key, i, e);
            }

    最后要注意的一點是,ThreadLocalMap的Entry是一個weakReference:

           /**
             * The entries in this hash map extend WeakReference, using
             * its main ref field as the key (which is always a
             * ThreadLocal object).  Note that null keys (i.e. entry.get()
             * == null) mean that the key is no longer referenced, so the
             * entry can be expunged from table.  Such entries are referred to
             * as "stale entries" in the code that follows.
             */
            static class Entry extends WeakReference<ThreadLocal> {
                /** The value associated with this ThreadLocal. */
                Object value;
     
                Entry(ThreadLocal k, Object v) {
                    super(k);
                    value = v;
                }
            }

    這里主要因為ThreadLocalMap的key是ThreadLocal對象,如果某個ThreadLocal對象所有的強引用沒有了,會利用weakref的功能把他回收掉,然后復用這個entry。

    考慮一下如果不用weakReference會出現(xiàn)什么情況:假設某個對象是這樣引用的

    private final ThreadLocal<String> globalName = new ThreadLocal<String>();

    注意沒有static,然后這個對象被不斷的new出來,然后死掉,每次ThreadLocalmap中都會多出一個entry,然后這個entry強引用一個ThreadLocal對象,ThreadLocalMap本身就沒有辦法確定哪個entry是不用了的,如果恰好這個線程是線程池中的,會存活很久,那就杯具了。

    ThreadLocalMap用了weakReference,失去強引用的ThreadLocal對象會在下次gc時被回收,然后ThreadLocalMap本身在get和set的時候會考察key為空的Entry,并復用它或者清除,從而避免內存泄露。

    這樣看來,HashMap也有一樣的問題,但為什么hashMap不這樣呢,因為hashMap的put是業(yè)務代碼操作的,因此如果有長期存活的HashMap,(比如static的)業(yè)務代碼put進去就有義務去remove,但ThreadLocal的put操作時ThreadLocal類干的,業(yè)務代碼不知道,因此也不會去做remove,而ThreadLocalMap本身不知道引用他的某個entry的key的對象什么時候死掉了,那么如果不用弱引用,就不知道這個ThreadLocal對象什么時候需要回收了。

    附:

    這里補充一下weakReference的用法供參考(當強引用不存在時,下次垃圾回收會回收弱引用所引用的對象):

    		Object o = new Object();
    		WeakReference<Object> ref = new WeakReference<Object>(o);
    		System.out.println(ref.get());
    		o=null;
    		System.gc();
    		System.out.println(ref.get());

    結果輸出:

     java.lang.Object@de6ced
     null

    4. FAQ

    4.1 為什么一般的ThreadLocal用法都要加static,如下:

    class Test {
        private static final ThreadLocal<String> globalName = new ThreadLocal<String>();
    }
    answer:事實上,不一定是要static,但使用它的對象在業(yè)務需要范圍類一定要是單例。因為根據前面的分析,ThreadLocalMap是以ThreadLocal對象為key的,如果Test類不是static,也不是單例的,那么兩個Test對象就有兩個key,取出來的數據肯定不同

     

    class TestThreadLocal{    
        public static void main(String[] args) {
    		Test t1 = new Test();
    		Test t2 = new Test();
     
    		t1.pool.set("a");
    		System.out.println(t1.pool.get());
    		System.out.println(t2.pool.get());
    	}
    }
    class Test{
    	public ThreadLocal pool = new ThreadLocal();
    }

    輸出將會是:a,null

    原因就無需多解釋了。唯一需要啰嗦的一點是,就算一般情況都是單例,上面那個weakreference還是必要的,因為作為框架代碼,不能保證正常使用的情況下一個線程有很多ThreadLocal,如果不用weakreference,就會有內存泄漏的風險,特別是針對線程池中的線程。

    posted on 2012-05-31 13:15 透明的魚 閱讀(590) 評論(0)  編輯  收藏


    只有注冊用戶登錄后才能發(fā)表評論。


    網站導航:
     
    主站蜘蛛池模板: 亚洲理论片中文字幕电影| 亚洲AV无码一区二区三区性色| 亚洲欧洲尹人香蕉综合| 国产精品小视频免费无限app| 99精品全国免费观看视频| 亚洲精品午夜国产VA久久成人| 亚洲精品亚洲人成在线| 久久久久久曰本AV免费免费| 中文字幕亚洲一区二区三区| 鲁死你资源站亚洲av| 亚洲精品无码MV在线观看| 成人免费午夜在线观看| 成人精品视频99在线观看免费| 国产免费私拍一区二区三区| 亚洲丰满熟女一区二区v| 久久这里只精品国产免费10| 亚洲日韩精品一区二区三区| eeuss草民免费| 亚洲一区二区三区国产精品无码| h视频在线观看免费完整版| 久久av无码专区亚洲av桃花岛| 午夜免费啪视频在线观看 | 四虎影视在线影院在线观看免费视频 | 日韩精品无码免费一区二区三区| 亚洲AV成人片色在线观看| 三根一起会坏掉的好痛免费三级全黄的视频在线观看 | 红杏亚洲影院一区二区三区| 手机在线看永久av片免费| 在线精品亚洲一区二区| 男女啪啪永久免费观看网站| 亚洲人成网站在线在线观看| 亚洲av无码国产精品夜色午夜| 精品无码国产污污污免费网站 | 男人的好免费观看在线视频| 久久精品视频免费| 亚洲精品永久www忘忧草| 怡红院亚洲怡红院首页| 91精品国产免费网站| 成人无码精品1区2区3区免费看| 国产精品亚洲二区在线| 久久夜色精品国产亚洲av|