国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

Netty學(xué)習(xí)之旅----ThreadLocal原理分析與性能優(yōu)化思考(思考篇)

2019-11-09 19:55:33
字體:
供稿:網(wǎng)友
1、java.lang.ThreadLocal概況ThreadLocal,本地線程變量,每個線程保留著一個共享變量的副本。其實我不太認(rèn)可每個線程保存共享變量的一個副本這個說法,而是ThreadLocal是線程上下文環(huán)境的一種實現(xiàn)方式而已。就以數(shù)據(jù)庫事務(wù)這一常用場景來舉例說明,比如每個線程需要訪問數(shù)據(jù)庫,就需要獲取數(shù)據(jù)庫的連接Connection對象,在實際中,我們會用數(shù)據(jù)庫連接池來重復(fù)利用Connection,首先線程池,這里是一個共享變量,線程池的實現(xiàn)必須保證多個線程同時從線程池中獲取Connection不會重復(fù),然后每個線程使用單獨(dú)的Connection,并且該Connection被一個線程占用后,其他線程壓根就不會使用到,也不會試圖去使用一個已經(jīng)被其他線程占用的Connection對象。由于一個線程在執(zhí)行過程中,可能需要多次操作數(shù)據(jù)庫,所以我們的設(shè)計就是一個線程在執(zhí)行過程中,只與一個Connection打交道,也就是整個線程的執(zhí)行過程(執(zhí)行環(huán)境)需要保存剛獲取的Connection,最簡單有效的辦法,就是把這個Connection保存在線程對象的某個屬性中,ThreadLocal就是干這事的。ThreadLocal并不是為這個Connection復(fù)制一份,多個線程都使用這個副本,不是這樣的,一個Connection對象在任意時刻,沒有被復(fù)制多份。我的觀點(diǎn):ThreadLocal是線程一個本地變量,是線程的執(zhí)行上下文。2、 從ThreadLocal get方法源碼分析其實現(xiàn)邏輯
/**     * 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();    //@1        ThreadLocalMap map = getMap(t);   //@2        if (map != null) {            ThreadLocalMap.Entry e = map.getEntry(this);     //@3            if (e != null)                return (T)e.value;        }        return setInitialValue();                      // @4    }代碼@1,獲取當(dāng)前線程。代碼@2,從當(dāng)前線程獲取ThreadLocalMap,ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    },這里是直接返回線程對象的threadLocals變量,有點(diǎn)意思吧,所以說ThreadLocal,是線程的本地變量,就是這層意思,真正存放數(shù)據(jù)的地方,就是線程對象本身,其實接下來的會更加有意思:我們進(jìn)入ThreadLocalMap源碼分析,得知,原來ThreadLocalMap就是一個Map結(jié)構(gòu)(K-V)鍵值對,關(guān)于里面的源碼就不一一分析了,ThreadLocalMap(ThreadLocal firstKey, Object firstValue),firstKey 為ThreadLocal,神奇吧,其實這也是為什么Thread的本地變量的數(shù)據(jù)類型為Map的原型,一個線程可以被多個ThreadLocal關(guān)聯(lián),每聲明一個,就在線程的threadLocals增加為一個鍵值對,key 為 ThreadLocal,而value為具體存放的對象。代碼@3,如果線程的ThreadLocalMap不為空,則直接返回對,否則進(jìn)入到代碼@4代碼@4,初始化并獲取放入ThreadLocal中的變量。上面就是ThreadLocal的核心設(shè)計理念,為了更加直觀的說明ThreadLocal原理,舉例說明:
-----------------------------------------------------------------------public class ThreadLocalDemo1 {   PRivate static final ThreadLocal<String> schemaLocal           = new ThreadLocal<String>();    public void test1() {        String a = schemaLocal.get();        ThreadLocalDemo2 demo2 = new ThreadLocalDemo2();        demo2.test(a);    }    public static void main(String[] args) {        // TODO Auto-generated method stub    }}public class ThreadLocalDemo2 {  private static final ThreadLocal<String> slocal = new ThreadLocal<String>();    public void test(String b) {        String a = slocal.get();        // 其他代碼        System.out.println(b);    }    public static void main(String[] args) {        // TODO Auto-generated method stub    }}public class TestMain {    public static void main(String[] args) {        // TODO Auto-generated method stub        ThreadLocalDemo1 d = new ThreadLocalDemo1();        d.test1();    }}//一個線程調(diào)用 ThreadLocalDemo1 的 test1方法,在這個執(zhí)行鏈中會涉及到兩個ThreadLocal變量,調(diào)用ThreadLocal的get方法,首先會獲取當(dāng)前線程,然后從當(dāng)前線程對象中獲取線程內(nèi)部屬性[ThreadLocal.ThreadLocalMap threadLocals = null;],然后從ThreadLocalMap中以ThreadLocal對象為鍵,從threadLocals map中獲取存放的值。線程的threadLocals值為{     ThreadLocalDemo1.schemaLocal  : 該變量中的值,     ThreadLocalDemo2.scloal : 存放在本線程中的值   }------------------------------------------------------------------------------------3、 ThreadLocal優(yōu)化思考    ThreadLocal的數(shù)據(jù)訪問算法,本質(zhì)上就是Map的訪問特性。 我在分析HashMap源碼的時候,已經(jīng)將HashMap的存儲結(jié)構(gòu)講解完畢,如有興趣,可以瀏覽一下我的博文:深入理解HashMap:http://blog.csdn.net/prestigeding/article/details/52861420,HashMap根據(jù)key的訪問速度效率是很快的,為什么呢?因為HashMap根據(jù)key的hash,然后會定位到內(nèi)部的數(shù)據(jù)槽(該數(shù)據(jù)是數(shù)組結(jié)構(gòu)),眾所周知,根據(jù)數(shù)組的下標(biāo)訪問,訪問速度是最快的,也就是說HashMap根據(jù)key的定位速度比LinkedList等都快,僅次于數(shù)組訪問方式,這是因為HashMap多了一步Hash定位槽的過程(當(dāng)然,如果有Hash沖突那就更慢了)。所以,如果在高并發(fā)場景下,需要進(jìn)一步優(yōu)化ThreadLocal的訪問性能,那就要從線程對象(Thread的threadLocals 數(shù)據(jù)結(jié)構(gòu)下手了,如果能將數(shù)據(jù)結(jié)構(gòu)修改為數(shù)組,然后每個ThreadLocal對象維護(hù)其下標(biāo)那就完美了)。是的,Netty框架就是為了高并發(fā)而生的,由于并發(fā)訪問的數(shù)量很大,一點(diǎn)點(diǎn)的性能優(yōu)化,就會帶來可觀的性能提升效應(yīng),Netty主要從如下兩個方面對ThreadLocal的實現(xiàn)進(jìn)行優(yōu)化1)線程對象直接提供 set、get方法,以便直接獲取線程本地存儲相關(guān)的變量屬性。2)將數(shù)據(jù)存儲基于數(shù)組存儲。4、Netty關(guān)于ThreadLocal機(jī)制的優(yōu)化由于ThreadLocal是JDK的原生實現(xiàn),通用性很強(qiáng),直接擴(kuò)展進(jìn)行定制化不是明智的選擇,故Netty在優(yōu)化ThreadLocal的方式是自己另起灶爐,實現(xiàn)ThreadLocal的語義。優(yōu)化方法如下:1)提供一個接口,F(xiàn)astThreadLocalaccess,并對線程池工廠類進(jìn)行定制,創(chuàng)建的線程繼承在java.lang.Thread類,并實現(xiàn)FastThreadLocalAccess接口,提供直接設(shè)置,獲取線程本地變量的方法。2)提供FastThreadLocal類,此類實現(xiàn)ThreadLocal相同的語義。3)提供InternalThreadLocalMap類,此類作用類同于java.lang.ThreadLocal.ThreadLocalMap類,用于線程存放真實數(shù)據(jù)的結(jié)構(gòu)。4.1 擴(kuò)展線程對象,提供set,get方法     通過定制的線程池工廠,創(chuàng)建的線程對象為擴(kuò)展后的線程對象,在Netty中對應(yīng)為FastThreadLocalThread,該類本身很簡單,值得大家注意的是其思想,jdk并發(fā)包中提供的線程池實現(xiàn)機(jī)制中,提供了線程創(chuàng)建的工廠的擴(kuò)展點(diǎn),這里就是其典型的實踐。     這里附上其源碼,不做解讀:
public class FastThreadLocalThread extends Thread implements FastThreadLocalAccess {    private InternalThreadLocalMap threadLocalMap;    public FastThreadLocalThread() { }    public FastThreadLocalThread(Runnable target) {        super(target);    }    public FastThreadLocalThread(ThreadGroup group, Runnable target) {        super(group, target);    }    public FastThreadLocalThread(String name) {        super(name);    }    public FastThreadLocalThread(ThreadGroup group, String name) {        super(group, name);    }    public FastThreadLocalThread(Runnable target, String name) {        super(target, name);    }    public FastThreadLocalThread(ThreadGroup group, Runnable target, String name) {        super(group, target, name);    }    public FastThreadLocalThread(ThreadGroup group, Runnable target, String name, long stackSize) {        super(group, target, name, stackSize);    }    /**     * Returns the internal data structure that keeps the thread-local variables bound to this thread.     * Note that this method is for internal use only, and thus is subject to change at any time.     */    @Override    public final InternalThreadLocalMap threadLocalMap() {        return threadLocalMap;    }    /**     * Sets the internal data structure that keeps the thread-local variables bound to this thread.     * Note that this method is for internal use only, and thus is subject to change at any time.     */    @Override    public final void setThreadLocalMap(InternalThreadLocalMap threadLocalMap) {        this.threadLocalMap = threadLocalMap;    }}4.2 FastThreadLocal與InternalThreadLocalMapInternalThreadLocalMap是線程存儲本地變量的數(shù)據(jù)結(jié)構(gòu),每個線程擁有自己的InternalThreadLocalMap,其作用與java.lang.ThreadLocal.ThreadLocalMap內(nèi)部類一樣,而FastThreadLocal,其語義與ThreadLocal一樣,對外表現(xiàn)與ThreadLocal一樣。再次重復(fù)一下,Netty的InternalThreadLocalMap內(nèi)部為數(shù)組,為什么是數(shù)組呢?線程本地變量,要從線程的執(zhí)行流的角度看,一個線程在執(zhí)行過程中,會經(jīng)過多個類,會有多個類中聲明有線程本地變量(參考上文說明ThreadLocal時候的舉例),所以此處的數(shù)組就是保留線程在整個線程的執(zhí)行過程中,不同的ThreadLocal變量中保存不同的數(shù)據(jù),java.lang.ThreadLocal.ThreadLocalMap內(nèi)部類的實現(xiàn)使用map結(jié)構(gòu),鍵為 ThreadLocal對象,而值為真正保存的變量值,InternalThreadLocalMap既然是數(shù)組,數(shù)組是一維的,數(shù)組最終肯定只能保存 真正要保持的變量值,那怎么區(qū)分不同的ThreadLocal在InternalThreadLocalMap中的下標(biāo)呢?Netty采用的方式是將下標(biāo)保存在FastThreadLocal中,我們知道,一般使用本地線程變量,F(xiàn)astThreadLocal的聲明方式,一般是類變量(靜態(tài)變量),諸如:private static final ThreadLocal aThreadLocal = new ThreadLocal();整個系統(tǒng)ThreadLocal的個數(shù)其實不會很多,每個FastThreadLocal在InternalThreadLocalMap的下標(biāo)(偏移量)在FastThreadLocal加載時候確定,并保持不變。并且每個InternalThreadLocal內(nèi)部數(shù)組的第一元素,存放系統(tǒng)運(yùn)行中的FastThreadLocal對象。InternalThreadLocalMap的內(nèi)部數(shù)據(jù)結(jié)構(gòu)為:/** Used by {@link FastThreadLocal} */Object[] indexedVariables;現(xiàn)在舉例說明上述理論,比如整個項目,有A,B,C,D,E5個類中各聲明了一個靜態(tài)的FastThreadLocal變量,類的加載順序為  A , B  , D, E, C那們 類A中的FastThreadLocal存放在線程變量InternalThreadLocalMap的下標(biāo)為1,B,為2,D為3,依次內(nèi)推,比如線程 T1,在一次請求過程中,需要用的A,E,C三個類中的FastThreadLocalMap,那么線程T1,的InternalThreadLocalMap的水庫中的下標(biāo)為1為A,下標(biāo)為2,3的元素為空,下標(biāo)為4為E,下標(biāo)5存放C中的變量值。每個線程InternalThreadLocalMap下標(biāo)為0的是一Set集合,存放的是系統(tǒng)運(yùn)行中有效的FastThreadLocal變量。根據(jù)這樣的存放后,F(xiàn)astThreadLocal 的get,set方法,都是根據(jù)下標(biāo)直接在InternalThreadLocalMap的數(shù)組中直接存儲,當(dāng)然,值得一提的是InternalThreadLocalMap中數(shù)組元素長度默認(rèn)為32,如果系統(tǒng)的FastThreadLocal的數(shù)量超過32個的話,會成倍擴(kuò)容。FastThreadLocal學(xué)習(xí)的入口,建議從set,get方法入手即可,知道上述原理后,源碼的閱讀應(yīng)該比較容易,就不做過多講解了。    如果愿意分享技術(shù)心得,歡迎加群:互聯(lián)網(wǎng)技術(shù)交流群 34265357
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 永康市| 延长县| 全南县| 科尔| 万山特区| 原平市| 四川省| 屯门区| 高碑店市| 固安县| 朝阳区| 聂拉木县| 永仁县| 林周县| 云浮市| 汝州市| 泉州市| 广昌县| 东莞市| 道真| 桂平市| 滕州市| 甘泉县| 西乡县| 自治县| 拜泉县| 当涂县| 大丰市| 光泽县| 嘉荫县| 安新县| 松桃| 延安市| 青阳县| 新乐市| 建瓯市| 济阳县| 柘荣县| 莱州市| 东城区| 丘北县|