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

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

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

    swzhebei

    常用鏈接

    統(tǒng)計(jì)

    最新評(píng)論

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

    ThreadLocal類(1)

    1.目的

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

    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.實(shí)現(xiàn)分析

    要實(shí)現(xiàn)上面這樣的功能,最簡(jiǎn)單的想法是用一個(gè)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);
    	}
    }

    這樣也能實(shí)現(xiàn)ThreadLocal的效果,但是有一個(gè)問題,當(dāng)對(duì)應(yīng)的線程消失后,map中對(duì)應(yīng)的線程值并不會(huì)被回收,從而造成內(nèi)存泄露。

    事實(shí)上ThreadLocal是這樣做的:

    每個(gè)Thread都有一個(gè)threadLocalMap,key是threadLocal對(duì)象,value是具體使用的值。ThreadLocal對(duì)象的get就是先取得當(dāng)前的Thread,然后從這個(gè)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();
        }

    注意這里如果取到?jīng)]有該線程對(duì)應(yīng)的值,會(huì)調(diào)用setInitialValue();,最終調(diào)用initialValue()生成一個(gè)值,這也是我們很多場(chǎng)景下要override這個(gè)方法的原因;

     

    下面看一下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的信息,最終是關(guān)聯(lián)到Thread上的,線程消失后,對(duì)應(yīng)的Thread對(duì)象也被回收,這時(shí)對(duì)應(yīng)的ThreadLocal對(duì)象(該線程部分)也會(huì)被回收。

    這里為什么是一個(gè)ThreadLocalMap呢,因?yàn)橐粋€(gè)線程可以有多個(gè)ThreadLocal變量,通過map.getEntry(this)取得對(duì)應(yīng)的某個(gè)具體的變量。

            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);
            }

    最后要注意的一點(diǎn)是,ThreadLocalMap的Entry是一個(gè)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;
                }
            }

    這里主要因?yàn)門hreadLocalMap的key是ThreadLocal對(duì)象,如果某個(gè)ThreadLocal對(duì)象所有的強(qiáng)引用沒有了,會(huì)利用weakref的功能把他回收掉,然后復(fù)用這個(gè)entry。

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

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

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

    ThreadLocalMap用了weakReference,失去強(qiáng)引用的ThreadLocal對(duì)象會(huì)在下次gc時(shí)被回收,然后ThreadLocalMap本身在get和set的時(shí)候會(huì)考察key為空的Entry,并復(fù)用它或者清除,從而避免內(nèi)存泄露。

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

    附:

    這里補(bǔ)充一下weakReference的用法供參考(當(dāng)強(qiáng)引用不存在時(shí),下次垃圾回收會(huì)回收弱引用所引用的對(duì)象):

    		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());

    結(jié)果輸出:

     java.lang.Object@de6ced
     null

    4. FAQ

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

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

     

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

    輸出將會(huì)是:a,null

    原因就無需多解釋了。唯一需要啰嗦的一點(diǎn)是,就算一般情況都是單例,上面那個(gè)weakreference還是必要的,因?yàn)樽鳛榭蚣艽a,不能保證正常使用的情況下一個(gè)線程有很多ThreadLocal,如果不用weakreference,就會(huì)有內(nèi)存泄漏的風(fēng)險(xiǎn),特別是針對(duì)線程池中的線程。

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


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


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 国产亚洲人成在线播放| 中文字幕免费观看| 亚洲AV无码日韩AV无码导航| 四虎成年永久免费网站| 羞羞视频免费网站含羞草| 亚洲AV无码久久精品狠狠爱浪潮 | 一个人看的www视频免费在线观看 一个人看的免费观看日本视频www | 热久久精品免费视频| 精品熟女少妇aⅴ免费久久| 亚洲国产成人久久三区| 亚洲人成网站色在线入口| 19禁啪啪无遮挡免费网站| 最新亚洲人成无码网站| 911精品国产亚洲日本美国韩国| 免费高清资源黄网站在线观看| 国产午夜无码精品免费看动漫| 久久亚洲精品国产亚洲老地址| 伊人婷婷综合缴情亚洲五月| 免费人成视频在线| 免费黄网站在线观看| 午夜亚洲国产精品福利| 亚洲码一区二区三区| 夜夜春亚洲嫩草影院| 国产片免费在线观看| 2020久久精品国产免费| 99麻豆久久久国产精品免费| 亚洲精品无码久久久久YW| 久久夜色精品国产噜噜噜亚洲AV| www.亚洲色图| 永久免费看bbb| 18禁无遮挡无码网站免费| 蜜桃AV无码免费看永久| 99精品免费视品| a毛片成人免费全部播放| 亚洲丰满熟女一区二区哦| 亚洲日韩中文字幕天堂不卡 | 亚洲欧美在线x视频| 亚洲AV无码无限在线观看不卡 | 亚洲综合中文字幕无线码| 99亚洲精品高清一二区| 国产成A人亚洲精V品无码性色|