国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

ThreadLocal就是這么簡(jiǎn)單

zhouzhou / 2310人閱讀

摘要:簡(jiǎn)要言之往中填充的變量屬于當(dāng)前線程,該變量對(duì)其他線程而言是隔離的。在辦理時(shí)又告訴機(jī)構(gòu)來(lái),把我的身份證房產(chǎn)證學(xué)生證通通給他。

前言

今天要研究的是ThreadLocal,這個(gè)我在一年前學(xué)習(xí)JavaWeb基礎(chǔ)的時(shí)候接觸過(guò)一次,當(dāng)時(shí)在baidu搜出來(lái)的第一篇博文ThreadLocal,在評(píng)論下很多開(kāi)發(fā)者認(rèn)為那博主理解錯(cuò)誤,給出了很多有關(guān)的鏈接來(lái)指正(可原博主可能沒(méi)上博客了,一直沒(méi)做修改)。我也去學(xué)習(xí)了一番,可惜的是當(dāng)時(shí)還沒(méi)有記錄的習(xí)慣,直到現(xiàn)在僅僅記住了一些當(dāng)時(shí)學(xué)過(guò)的皮毛。

因此,做一些技術(shù)的記錄是很重要的~同時(shí),ThreadLocal也是面試非常常見(jiàn)的面試題,對(duì)Java開(kāi)發(fā)者而言也是一個(gè)必要掌握的知識(shí)點(diǎn)~

當(dāng)然了,如果我有寫(xiě)錯(cuò)的地方請(qǐng)大家多多包涵,歡迎在評(píng)論下留言指正~

一、什么是ThreadLocal
聲明:本文使用的是JDK 1.8

首先我們來(lái)看一下JDK的文檔介紹:

/**
 * This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.  {@code ThreadLocal} instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).
 * 
 * 

For example, the class below generates unique identifiers local to each * thread. * A thread"s id is assigned the first time it invokes {@code ThreadId.get()} * and remains unchanged on subsequent calls. */

結(jié)合我的總結(jié)可以這樣理解:ThreadLocal提供了線程的局部變量,每個(gè)線程都可以通過(guò)set()get()來(lái)對(duì)這個(gè)局部變量進(jìn)行操作,但不會(huì)和其他線程的局部變量進(jìn)行沖突,實(shí)現(xiàn)了線程的數(shù)據(jù)隔離~。

簡(jiǎn)要言之:往ThreadLocal中填充的變量屬于當(dāng)前線程,該變量對(duì)其他線程而言是隔離的。

二、為什么要學(xué)習(xí)ThreadLocal?

從上面可以得出:ThreadLocal可以讓我們擁有當(dāng)前線程的變量,那這個(gè)作用有什么用呢???

2.1管理Connection

最典型的是管理數(shù)據(jù)庫(kù)的Connection:當(dāng)時(shí)在學(xué)JDBC的時(shí)候,為了方便操作寫(xiě)了一個(gè)簡(jiǎn)單數(shù)據(jù)庫(kù)連接池,需要數(shù)據(jù)庫(kù)連接池的理由也很簡(jiǎn)單,頻繁創(chuàng)建和關(guān)閉Connection是一件非常耗費(fèi)資源的操作,因此需要?jiǎng)?chuàng)建數(shù)據(jù)庫(kù)連接池~

那么,數(shù)據(jù)庫(kù)連接池的連接怎么管理呢??我們交由ThreadLocal來(lái)進(jìn)行管理。為什么交給它來(lái)管理呢??ThreadLocal能夠?qū)崿F(xiàn)當(dāng)前線程的操作都是用同一個(gè)Connection,保證了事務(wù)!

當(dāng)時(shí)候?qū)懙拇a:

public class DBUtil {
    //數(shù)據(jù)庫(kù)連接池
    private static BasicDataSource source;

    //為不同的線程管理連接
    private static ThreadLocal local;


    static {
        try {
            //加載配置文件
            Properties properties = new Properties();

            //獲取讀取流
            InputStream stream = DBUtil.class.getClassLoader().getResourceAsStream("連接池/config.properties");

            //從配置文件中讀取數(shù)據(jù)
            properties.load(stream);

            //關(guān)閉流
            stream.close();

            //初始化連接池
            source = new BasicDataSource();

            //設(shè)置驅(qū)動(dòng)
            source.setDriverClassName(properties.getProperty("driver"));

            //設(shè)置url
            source.setUrl(properties.getProperty("url"));

            //設(shè)置用戶名
            source.setUsername(properties.getProperty("user"));

            //設(shè)置密碼
            source.setPassword(properties.getProperty("pwd"));

            //設(shè)置初始連接數(shù)量
            source.setInitialSize(Integer.parseInt(properties.getProperty("initsize")));

            //設(shè)置最大的連接數(shù)量
            source.setMaxActive(Integer.parseInt(properties.getProperty("maxactive")));

            //設(shè)置最長(zhǎng)的等待時(shí)間
            source.setMaxWait(Integer.parseInt(properties.getProperty("maxwait")));

            //設(shè)置最小空閑數(shù)
            source.setMinIdle(Integer.parseInt(properties.getProperty("minidle")));

            //初始化線程本地
            local = new ThreadLocal<>();


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        if(local.get()!=null){
            return local.get();
        }else{
        
          //獲取Connection對(duì)象
          Connection connection = source.getConnection();

          //把Connection放進(jìn)ThreadLocal里面
          local.set(connection);

          //返回Connection對(duì)象
          return connection;
       }
    }

    //關(guān)閉數(shù)據(jù)庫(kù)連接
    public static void closeConnection() {
        //從線程中拿到Connection對(duì)象
        Connection connection = local.get();

        try {
            if (connection != null) {
                //恢復(fù)連接為自動(dòng)提交
                connection.setAutoCommit(true);

                //這里不是真的把連接關(guān)了,只是將該連接歸還給連接池
                connection.close();

                //既然連接已經(jīng)歸還給連接池了,ThreadLocal保存的Connction對(duì)象也已經(jīng)沒(méi)用了
                local.remove();

            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


}

同樣的,Hibernate對(duì)Connection的管理也是采用了相同的手法(使用ThreadLocal,當(dāng)然了Hibernate的實(shí)現(xiàn)是更強(qiáng)大的)~

2.2避免一些參數(shù)傳遞

避免一些參數(shù)的傳遞的理解可以參考一下Cookie和Session:

每當(dāng)我訪問(wèn)一個(gè)頁(yè)面的時(shí)候,瀏覽器都會(huì)幫我們從硬盤(pán)中找到對(duì)應(yīng)的Cookie發(fā)送過(guò)去。

瀏覽器是十分聰明的,不會(huì)發(fā)送別的網(wǎng)站的Cookie過(guò)去,只帶當(dāng)前網(wǎng)站發(fā)布過(guò)來(lái)的Cookie過(guò)去

瀏覽器就相當(dāng)于我們的ThreadLocal,它僅僅會(huì)發(fā)送我們當(dāng)前瀏覽器存在的Cookie(ThreadLocal的局部變量),不同的瀏覽器對(duì)Cookie是隔離的(Chrome,Opera,IE的Cookie是隔離的【在Chrome登陸了,在IE你也得重新登陸】),同樣地:線程之間ThreadLocal變量也是隔離的....

那上面避免了參數(shù)的傳遞了嗎??其實(shí)是避免了。Cookie并不是我們手動(dòng)傳遞過(guò)去的,并不需要寫(xiě)來(lái)進(jìn)行傳遞參數(shù)...

在編寫(xiě)程序中也是一樣的:日常中我們要去辦理業(yè)務(wù)可能會(huì)有很多地方用到身份證,各類(lèi)證件,每次我們都要掏出來(lái)很麻煩

    // 咨詢時(shí)要用身份證,學(xué)生證,房產(chǎn)證等等....
    public void consult(IdCard idCard,StudentCard studentCard,HourseCard hourseCard){

    }

    // 辦理時(shí)還要用身份證,學(xué)生證,房產(chǎn)證等等....
    public void manage(IdCard idCard,StudentCard studentCard,HourseCard hourseCard) {

    }

    //......

而如果用了ThreadLocal的話,ThreadLocal就相當(dāng)于一個(gè)機(jī)構(gòu),ThreadLocal機(jī)構(gòu)做了記錄你有那么多張證件。用到的時(shí)候就不用自己掏了,問(wèn)機(jī)構(gòu)拿就可以了。

在咨詢時(shí)的時(shí)候就告訴機(jī)構(gòu):來(lái),把我的身份證、房產(chǎn)證、學(xué)生證通通給他。在辦理時(shí)又告訴機(jī)構(gòu):來(lái),把我的身份證、房產(chǎn)證、學(xué)生證通通給他。...

    // 咨詢時(shí)要用身份證,學(xué)生證,房產(chǎn)證等等....
    public void consult(){

        threadLocal.get();
    }

    // 辦理時(shí)還要用身份證,學(xué)生證,房產(chǎn)證等等....
    public void takePlane() {
        threadLocal.get();
    }

這樣是不是比自己掏方便多了。

當(dāng)然了,ThreadLocal可能還會(huì)有其他更好的作用,如果知道的同學(xué)可在評(píng)論留言哦~~~

三、ThreadLocal實(shí)現(xiàn)的原理

想要更好地去理解ThreadLocal,那就得翻翻它是怎么實(shí)現(xiàn)的了~~~

聲明:本文使用的是JDK 1.8

首先,我們來(lái)看一下ThreadLocal的set()方法,因?yàn)槲覀円话闶褂枚际莕ew完對(duì)象,就往里邊set對(duì)象了

    public void set(T value) {

        // 得到當(dāng)前線程對(duì)象
        Thread t = Thread.currentThread();
        
        // 這里獲取ThreadLocalMap
        ThreadLocalMap map = getMap(t);

        // 如果map存在,則將當(dāng)前對(duì)象threadLocal作為key,要存儲(chǔ)的對(duì)象作為value存到map里面去
        if (map != null)
            map.set(this, value);
        else
            // 如果map不存在,則在當(dāng)前線程中創(chuàng)建出一個(gè)ThreadLocalMap!
            createMap(t, value);
    }

上面有個(gè)ThreadLocalMap,我們?nèi)タ纯催@是什么?

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> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }
        //....很長(zhǎng)
}

通過(guò)上面我們可以發(fā)現(xiàn)的是ThreadLocalMap是ThreadLocal的一個(gè)內(nèi)部類(lèi)。用Entry類(lèi)來(lái)進(jìn)行存儲(chǔ)

我們的值都是存儲(chǔ)到這個(gè)Map上的,key是當(dāng)前ThreadLocal對(duì)象

如果該Map不存在,則初始化一個(gè):

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

如果該Map存在,則從Thread中獲取

    /**
     * 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;
    }

Thread維護(hù)了ThreadLocalMap變量

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

從上面又可以看出,ThreadLocalMap是在ThreadLocal中使用內(nèi)部類(lèi)來(lái)編寫(xiě)的,但對(duì)象的引用是在Thread中

于是我們可以總結(jié)出:Thread為每個(gè)線程維護(hù)了ThreadLocalMap這么一個(gè)Map,而ThreadLocalMap的key是ThreadLocal對(duì)象本身,value則是要存儲(chǔ)的對(duì)象

有了上面的基礎(chǔ),我們看get()方法就一點(diǎn)都不難理解了:

    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();
    }
3.1ThreadLocal原理總結(jié)

每個(gè)Thread維護(hù)著一個(gè)ThreadLocalMap的引用

ThreadLocalMap是ThreadLocal的內(nèi)部類(lèi),用Entry來(lái)進(jìn)行存儲(chǔ)

調(diào)用ThreadLocal的set()方法時(shí),實(shí)際上就是往ThreadLocalMap設(shè)置值,key是ThreadLocal對(duì)象,值是傳遞進(jìn)來(lái)的對(duì)象

調(diào)用ThreadLocal的get()方法時(shí),實(shí)際上就是往ThreadLocalMap獲取值,key是ThreadLocal對(duì)象

ThreadLocal本身并不存儲(chǔ)值,它只是作為一個(gè)key來(lái)讓線程從ThreadLocalMap獲取value

正因?yàn)檫@個(gè)原理,所以ThreadLocal能夠?qū)崿F(xiàn)“數(shù)據(jù)隔離”,獲取當(dāng)前線程的局部變量值,不受其他線程影響~

四、避免內(nèi)存泄露

我們來(lái)看一下ThreadLocal的對(duì)象關(guān)系引用圖:

ThreadLocal內(nèi)存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一樣長(zhǎng),如果沒(méi)有手動(dòng)刪除對(duì)應(yīng)key就會(huì)導(dǎo)致內(nèi)存泄漏,而不是因?yàn)槿跻?/strong>。

想要避免內(nèi)存泄露就要手動(dòng)remove()掉

五、總結(jié)

ThreadLocal這方面的博文真的是數(shù)不勝數(shù),隨便一搜就很多很多~站在前人的肩膀上總結(jié)了這篇博文~

最后要記住的是:ThreadLocal設(shè)計(jì)的目的就是為了能夠在當(dāng)前線程中有屬于自己的變量,并不是為了解決并發(fā)或者共享變量的問(wèn)題

如果看得不夠過(guò)癮,覺(jué)得不夠深入的同學(xué)可參考下面的鏈接,很多的博主還開(kāi)展了一些擴(kuò)展知識(shí),我就不一一展開(kāi)了~

參考博文:

http://blog.xiaohansong.com/2016/08/06/ThreadLocal-memory-leak/

https://www.cnblogs.com/zhangjk1993/archive/2017/03/29/6641745.html#_label2

http://www.cnblogs.com/dolphin0520/p/3920407.html

http://www.cnblogs.com/dolphin0520/p/3920407.html

http://www.iteye.com/topic/103804

https://www.cnblogs.com/xzwblog/p/7227509.html

https://blog.csdn.net/u012834750/article/details/71646700

https://blog.csdn.net/winwill2012/article/details/71625570

https://juejin.im/post/5a64a581f265da3e3b7aa02d

如果文章有錯(cuò)的地方歡迎指正,大家互相交流。習(xí)慣在微信看技術(shù)文章,想要獲取更多的Java資源的同學(xué),可以關(guān)注微信公眾號(hào):Java3y

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/68903.html

相關(guān)文章

  • 【源起Netty 外傳】FastThreadLocal怎么Fast?

    摘要:實(shí)現(xiàn)原理淺談幫助理解的示意圖中有一屬性,類(lèi)型是的靜態(tài)內(nèi)部類(lèi)。剛剛說(shuō)過(guò),是一個(gè)中的靜態(tài)內(nèi)部類(lèi),則是的內(nèi)部節(jié)點(diǎn)。這個(gè)會(huì)在線程中,作為其屬性初始是一個(gè)數(shù)組的索引,達(dá)成與類(lèi)似的效果。的方法被調(diào)用時(shí),會(huì)根據(jù)記錄的槽位信息進(jìn)行大掃除。 概述 FastThreadLocal的類(lèi)名本身就充滿了對(duì)ThreadLocal的挑釁,快男FastThreadLocal是怎么快的?源碼中類(lèi)注釋坦白如下: /** ...

    gxyz 評(píng)論0 收藏0
  • python學(xué)習(xí)筆記 - ThreadLocal

    摘要:為了避免改亂為,我們?cè)谇懊嬉呀?jīng)提到說(shuō)要加鎖。僅供一個(gè)線程使用,線程間相互不影響。例如下列程序中函數(shù)中定義的變量就是局部變量。所有綁定的參數(shù)都是線程隔離的。下面展示一下代碼創(chuàng)建一個(gè)全局的對(duì)象初始化一個(gè)線程內(nèi)變量,該變量線程間互不影響。 我們?cè)诰帉?xiě)多線程程序的時(shí)候,往往會(huì)遇到兩種類(lèi)型的變量。 一種是全局變量,多個(gè)線程共享。為了避免改亂為,我們?cè)谇懊嬉呀?jīng)提到說(shuō)要加鎖。 一種是局部變量。僅供...

    Berwin 評(píng)論0 收藏0
  • (八)ThreadLocal的使用及原理分析

    摘要:什么是,簡(jiǎn)單翻譯過(guò)來(lái)就是本地線程,但是直接這么翻譯很難理解的作用,如果換一種說(shuō)法,可以稱為線程本地存儲(chǔ)。魔數(shù)的選取和斐波那契散列有關(guān),對(duì)應(yīng)的十進(jìn)制為。而斐波那契散列的乘數(shù)可以用如果把這個(gè)值給轉(zhuǎn)為帶符號(hào)的,則會(huì)得到。 什么是ThreadLocal ThreadLocal,簡(jiǎn)單翻譯過(guò)來(lái)就是本地線程,但是直接這么翻譯很難理解ThreadLocal的作用,如果換一種說(shuō)法,可以稱為線程本地存儲(chǔ)。...

    asoren 評(píng)論0 收藏0
  • Java并發(fā)編程之原子性操作

    摘要:將與當(dāng)前線程建立一對(duì)一關(guān)系的值移除。為了讓方法里的操作具有原子性,也就是在一個(gè)線程執(zhí)行這一系列操作的同時(shí)禁止其他線程執(zhí)行這些操作,提出了鎖的概念。 上頭一直在說(shuō)以線程為基礎(chǔ)的并發(fā)編程的好處了,什么提高處理器利用率啦,簡(jiǎn)化編程模型啦。但是磚家們還是認(rèn)為并發(fā)編程是程序開(kāi)發(fā)中最不可捉摸、最詭異、最扯犢子、最麻煩、最?lèi)盒摹⒆钚臒⒆钊菀壮鲥e(cuò)、最不符合社會(huì)主義核心價(jià)值觀的一個(gè)部分~ 造成這么多最...

    instein 評(píng)論0 收藏0
  • Java線程有哪些不太為人所知的技巧與用法?

    摘要:從本文中你將會(huì)看到線程提供的一些不太常用的方法及技術(shù)。最大的好處就是你所設(shè)定的優(yōu)先級(jí)能在每個(gè)線程獲得的時(shí)間上有所體現(xiàn),不過(guò)完全依賴于線程優(yōu)先級(jí)的做法是不推薦的。這省掉了正常關(guān)閉線程的那些麻煩事,可以立即將線程結(jié)束掉。 轉(zhuǎn)載出處:Java線程的5個(gè)使用技巧英文原文鏈接 蘿卜白菜各有所愛(ài)。像我就喜歡Java。學(xué)無(wú)止境,這也是我喜歡它的一個(gè)原因。日常工作中你所用到的工具,通常都有些你從來(lái)沒(méi)...

    freewolf 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<