1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        從獲取用戶信息來(lái)看ThreadLocal做了些什么

        共 7610字,需瀏覽 16分鐘

         ·

        2021-07-21 20:41

        系統(tǒng)登陸后后臺(tái)service是怎么獲取用戶信息的呢,說(shuō)實(shí)話,我也不知道,最近在做項(xiàng)目時(shí)遇到這么一個(gè)問(wèn)題,就認(rèn)真思考了下,在SpringMVC中找到了答案,就寫下來(lái)記錄下吧


        在項(xiàng)目中有個(gè)WebUtil工具,這里面是有獲取用戶信息的方法的,但我是要找到他是怎么保存并獲取這個(gè)用戶信息的,當(dāng)然不能只看的這么淺顯


        這個(gè)工具類中有個(gè)方法是獲取request請(qǐng)求的,我很好奇,獲取請(qǐng)求,它是怎么做到的,系統(tǒng)每秒少則幾千,多則上萬(wàn)的請(qǐng)求并發(fā),它獲取的請(qǐng)求到底是哪個(gè)請(qǐng)求,是我想要的那個(gè)嗎?

         public static HttpServletRequest getRequest() {    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();    return requestAttributes == null ? null :((ServletRequestAttributes)requestAttributes).getRequest();  }

        跟著就可以看到一個(gè)請(qǐng)求上下文容器RequestContextHolder,getRequestAttributes獲取了請(qǐng)求屬性,這個(gè)請(qǐng)求屬性不重要,反正不是我想要看到的,就看看這個(gè)容器里有啥吧

        private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal<>("Request attributes");private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal<>("Request context");/** * Return the RequestAttributes currently bound to the thread. * @return the RequestAttributes currently bound to the thread, * or {@code null} if none bound */@Nullablepublic static RequestAttributes getRequestAttributes() {RequestAttributes attributes = requestAttributesHolder.get();if (attributes == null) {attributes = inheritableRequestAttributesHolder.get();}return attributes;}

        代碼里有個(gè)稍微熟悉一點(diǎn)的類了,很熟悉但不完全熟悉。NamedThreadLocal和NamedInheritableThreadLocal

        我以前也不知道這兩個(gè)類是啥,我只知道ThreadLocal。

        先看下ThreadLocal是啥吧

        ThreadLocal看名字指的是線程本地的變量,也就是說(shuō)由ThreadLocal保存的變量是屬于當(dāng)前線程的,這個(gè)變量對(duì)于其他變量是隔離的,是不透明的,ThreaLocal為每一個(gè)線程都保存了一份這個(gè)變量的副本,那么每個(gè)線程就可以訪問(wèn)自己線程的副本變量了,怎么感覺(jué)是在說(shuō)廢話呢?

        來(lái)看下源碼是怎么寫的吧,源碼中主要的兩個(gè)方法就是set方法和get方法

        public class Thread implements Runnable {
        ThreadLocal.ThreadLocalMap threadLocals = null;
        ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; }
        public T get() {   //獲取當(dāng)前線程       Thread t = Thread.currentThread();   //從當(dāng)前線程中獲取線程變量ThreadLocalMap       ThreadLocalMap map = getMap(t);       if (map != null) {           //從map中根據(jù)key(也就是當(dāng)前ThreadLocal對(duì)象)獲取其對(duì)應(yīng)的Entry節(jié)點(diǎn)           ThreadLocalMap.Entry e = map.getEntry(this);           if (e != null) {               @SuppressWarnings("unchecked")               T result = (T)e.value;               return result;          }      }   //初始化當(dāng)前線程的ThredLocalMap或者當(dāng)前ThreadLocal對(duì)應(yīng)的屬性值   //這個(gè)方法和set()方法類似,但是這個(gè)方法的默認(rèn)value值是null       return setInitialValue();}
        /***set()方法和setInitialValue()類似,但是set()方法是去設(shè)置ThreadLocal對(duì)應(yīng)的值*如果當(dāng)前線程的ThreadLocalMap為null,將會(huì)先創(chuàng)建map再設(shè)置第一個(gè)值*/public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value);}

        我們?cè)賮?lái)看下上面所說(shuō)的ThreadLocalMap和Entry是啥吧

        static class ThreadLocalMap {
        /** * 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; } }
        /** * The initial capacity -- MUST be a power of two. */ private static final int INITIAL_CAPACITY = 16;
        /** * The table, resized as necessary. * table.length MUST always be a power of two. */ private Entry[] table;

        /** * Construct a new map initially containing (firstKey, firstValue). * ThreadLocalMaps are constructed lazily, so we only create * one when we have at least one entry to put in it. */ ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); }

        /** * Get the entry associated with key. This method * itself handles only the fast path: a direct hit of existing * key. It otherwise relays to getEntryAfterMiss. This is * designed to maximize performance for direct hits, in part * by making this method readily inlinable. * * @param key the thread local object * @return the entry associated with key, or null if no such */ 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); }
        /** * Set the value associated with key. * * @param key the thread local object * @param value the value to be set */ private void set(ThreadLocal<?> key, Object value) {
        // We don't use a fast path as with get() because it is at // least as common to use set() to create new entries as // it is to replace existing ones, in which case, a fast // path would fail more often than not.
        Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1);
        for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get();
        if (k == key) { e.value = value; return; }
        if (k == null) { replaceStaleEntry(key, value, i); return; } }
        tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
        /** * Remove the entry for key. */ private void remove(ThreadLocal<?> key) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { if (e.get() == key) { e.clear(); expungeStaleEntry(i); return; } } }

        }

        ThreadLocalMap是ThreadLocal的靜態(tài)內(nèi)部類,而Entry是ThreadLocalMap的靜態(tài)內(nèi)部類(禁止套娃警告)


        ThreadLocalMap的作用就是為了保存多個(gè)ThreadLocal變量,以ThreadLocal對(duì)象為key,屬性值為value


        從上面源碼可以看到ThreadLocalMap中真正保存持有這些ThreadLocal變量的對(duì)象是一個(gè)Entry數(shù)組對(duì)象


        看著ThreadLocal風(fēng)風(fēng)火火,其實(shí)它實(shí)際的用處就是hash出一個(gè)index值,這個(gè)index表示ThreadLocal屬性在Entry數(shù)組中的位置

        但是如果hash值一樣的話,那不就產(chǎn)生hash沖突了嗎?它是這樣做的

        采用線性探測(cè)的方法一個(gè)一個(gè)的探測(cè)當(dāng)前位置是否存在Entry對(duì)象,如果有,就去比對(duì)ThreadLocal對(duì)象是否與Entry中取出的key一致。


        如果一致就替換當(dāng)前key對(duì)應(yīng)的屬性值;如果不一樣,則調(diào)用replaceStaleEntry來(lái)設(shè)置值,同時(shí)會(huì)去清除那些key為空的value,以避免產(chǎn)生內(nèi)存泄漏問(wèn)題,也會(huì)將該Entry置為null,以備下次被使用

        Entry中的鍵使用WeakReference修飾的,當(dāng)ThreadLocal不再被使用時(shí),將會(huì)及時(shí)被回收,但是Entry中value是強(qiáng)引用,這樣的話Entry仍然會(huì)一直存在于內(nèi)存中,及時(shí)該Entry對(duì)象已經(jīng)形同虛設(shè),所以Java8中對(duì)此作了優(yōu)化,在ThreadLocal的get()、set()、remove()調(diào)用時(shí),會(huì)去清空那些key為空但是value不為空的Entry對(duì)象,避免發(fā)生內(nèi)存泄漏問(wèn)題


        每個(gè)ThreadLocal只能保存一個(gè)變量副本,如果想要一個(gè)線程能夠保存多個(gè)副本以上,就需要?jiǎng)?chuàng)建多個(gè)ThreadLocal。

        ThreadLocal內(nèi)部的ThreadLocalMap鍵為弱引用,會(huì)有內(nèi)存泄漏的風(fēng)險(xiǎn)。

        每次使用完ThreadLocal,都調(diào)用它的remove()方法,清除數(shù)據(jù)。


        Thread還持有一個(gè)inheritableThreadLocals引用,有興趣的同學(xué)可以自行研究下



        瀏覽 45
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲色秘书 | 国产在线第三页 | 人妖无码 | 天天超碰 | 天天好屄 | 色五月婷婷丁香花 | 桥本爱实大尺度三级 | 国产日韩欧美一区二区 | 女人18毛片AAA片水真多 一区二区三区成人 | 欧美视频手机在线 |