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

資訊專欄INFORMATION COLUMN

ThreadLocal基本原理及運(yùn)用

VEIGHTZ / 2841人閱讀

摘要:基本原理線程本地變量是和線程相關(guān)的變量,一個(gè)線程則一份數(shù)據(jù)。其中為聲明的對(duì)象。對(duì)于一個(gè)對(duì)象倘若沒(méi)有成員變量,單例非常簡(jiǎn)單,不用去擔(dān)心多線程同時(shí)對(duì)成員變量修改而產(chǎn)生的線程安全問(wèn)題。并且還不能使用單例模式,因?yàn)槭遣荒芏嗑€程訪問(wèn)的。

ThreadLocal簡(jiǎn)述

下面我們看一下ThreadLocal類的官方注釋。

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).

大致的意思是,ThreadLocal提供本地線程變量。這個(gè)變量里面的值(通過(guò)get方法獲取)是和其他線程分割開(kāi)來(lái)的,變量的值只有當(dāng)前線程能訪問(wèn)到,不像一般的類型比如Person,Student類型的變量,只要訪問(wèn)到聲明該變量的對(duì)象,即可訪問(wèn)其全部?jī)?nèi)容,而且各個(gè)線程的訪問(wèn)的數(shù)據(jù)是無(wú)差別的。Thread的典型應(yīng)用是提供一個(gè)與程序運(yùn)行狀態(tài)相關(guān)靜態(tài)變量,比如一次訪問(wèn)回話的表示符號(hào):USERID,或者一次事務(wù)里面的事務(wù)id:Transaction ID。

基本原理

線程本地變量是和線程相關(guān)的變量,一個(gè)線程則一份數(shù)據(jù)。我們通過(guò)ThreadLocal保存的數(shù)據(jù)最終是保存在Thread類的ThreadLocalMap threadLocals變量中。ThreadlocalMap是一個(gè)Map結(jié)構(gòu),其中key為我們聲明的ThreadLocal對(duì)象,value即為我們使用ThreadLocal保存的線程本地變量.

當(dāng)我們調(diào)用ThreadLocal變量set方法時(shí),那么為將TheadLocal作為key,set方法的參數(shù)做為value保存在當(dāng)前線程的threadLocals中.調(diào)用get方法時(shí)類似,調(diào)用get方法時(shí),會(huì)去Thread的threadLocals中去尋找key為ThreadLocal 變量的值

源碼如下:

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

// ThreadLocal set get方法

/**
 * Sets the current thread"s copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * override this method, relying solely on the {@link #initialValue}
 * method to set the values of thread-locals.
 *
 * @param value the value to be stored in the current thread"s copy of
 *        this thread-local.
 */
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);// getMap方法即去獲取當(dāng)前線程的ThreadLocalMap變量。
    if (map != null)
        map.set(this, value);//以this(ThreadLocal本身)為Key,參數(shù)value為值進(jìn)行保存
    else
        createMap(t, value);
}

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


/**
 * 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) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

下面是測(cè)試代碼:

static ThreadLocal stringThreadLocal = new ThreadLocal<>();

@Test
public  void test01(){
    Thread thread1 = new Thread(){
        @Override
        public void run() {
            stringThreadLocal.set("threadName===>"+Thread.currentThread().getName());
            System.out.println(this.getName()+" thread get the value:"+stringThreadLocal.get());

        }
    };
    Thread thread2 = new Thread(){
        @Override
        public void run() {
            stringThreadLocal.set("threadName===>"+Thread.currentThread().getName());
            System.out.println(this.getName()+" thread get the value:"+stringThreadLocal.get());

        }
    };
    Thread thread3 = new Thread(){
        @Override
        public void run() {
            stringThreadLocal.set("threadName===>"+Thread.currentThread().getName());
            System.out.println(this.getName()+" thread get the value:"+stringThreadLocal.get());
        }
    };

    thread1.start();
    thread2.start();
    thread3.start();
    System.out.println("main線程調(diào)用set方法之前:"+stringThreadLocal.get());
    stringThreadLocal.set("main 線程set的值");
    System.out.println("main線程調(diào)用set方法之后:"+stringThreadLocal.get());
}

可以看到不同線程設(shè)置的值在該線程是能夠正確的取到。由于Thread的threadLocals變量只能在Thread所在的包下才能夠訪問(wèn),因此不能對(duì)該變量進(jìn)行直接訪問(wèn)以驗(yàn)證設(shè)置的值在Thread.currentThread對(duì)象里面。但如果你調(diào)試以上代碼,設(shè)置值之后訪問(wèn)Thread.currentThread.threadLocals會(huì)看到之前設(shè)置的值。其中key為聲明的ThreadLocal對(duì)象。

ThreadLocal進(jìn)行參數(shù)傳遞

這算是比較正統(tǒng)的ThreadLocal用法,這可能也是ThreadLocal設(shè)計(jì)的初衷:用來(lái)保存于狀態(tài)相關(guān)的變量,比如訪問(wèn)者的用戶信息,事務(wù)的事務(wù)標(biāo)識(shí)。這里演示一下使用ThreadLocal來(lái)傳遞用戶信息,實(shí)際上當(dāng)前流行的大部分權(quán)限框架都是使用的ThreadLocal變量來(lái)保存用戶信息的。

下面是測(cè)試代碼:

//參數(shù)傳遞測(cè)試

    @Test
    public void test02(){
        //參數(shù)主要利用ThreadLocal是線程的局部變量,只要在同一個(gè)線程中,之前設(shè)置的值后面就能取到,從而達(dá)到參數(shù)值傳遞的效果。
        //在前面在線程變量中添加值
        stringThreadLocal.set("name");
        paramThreadLocal.set(new HashMap(){
            {
                put("id","1");
                put("name","xiaoTOT");
                put("gender","M");
            }
        });
        testParam();
    }

    private void testParam() {
        //從線程本地變量獲取參數(shù)
        Map map = paramThreadLocal.get();
        map.forEach((key,value)->{
            System.out.println("key:"+key+" & value="+value);
        });
    }
    
ThreadLocal改造改造在單例模式中的運(yùn)用

單例模式的好處無(wú)用質(zhì)疑,可以減少對(duì)象的創(chuàng)建。對(duì)于那些創(chuàng)建非常費(fèi)時(shí)的對(duì)象尤其明顯。并且如果能夠用單例解決的問(wèn)題經(jīng)歷使用單例解決,這樣能減輕運(yùn)行時(shí)的壓力。

對(duì)于一個(gè)對(duì)象倘若沒(méi)有成員變量,單例非常簡(jiǎn)單,不用去擔(dān)心多線程同時(shí)對(duì)成員變量修改而產(chǎn)生的線程安全問(wèn)題。

對(duì)于一個(gè)擁有成員變量的對(duì)象使用單例就需要考慮到線程安全問(wèn)題。多線程訪問(wèn)又可以分為下面兩個(gè)方面:

a:成員變量需要多線程同步,比如賬戶對(duì)象(ACCOUNT)中的成員變量余額(amount).amount成員變量需要在多線程的訪問(wèn)下保證各個(gè)線程保證絕對(duì)的同步,即無(wú)論什么時(shí)候線程內(nèi)的值都是一樣。我們可以通過(guò)加同步關(guān)鍵字synchronized,volatile來(lái)解決。

b,成員變量不需要線程同步,每個(gè)線程訪問(wèn)自己線程內(nèi)部的對(duì)象。比如一個(gè)服務(wù)類對(duì)數(shù)據(jù)庫(kù)的鏈接成員變量,每個(gè)線程分配一個(gè)連接即可。類似這種場(chǎng)景,我們最簡(jiǎn)單的方式是使用多例模式來(lái)解決。單更好的方式是通過(guò)threadLocal來(lái)解決。

下面是使用ThreadLocal改造單例模式的示例:

//ThreadLocal在單例模式改造的運(yùn)用
@Test
public  void test03(){
    //單例模式的好處無(wú)用質(zhì)疑,可以減少對(duì)象的創(chuàng)建。對(duì)于那些創(chuàng)建非常費(fèi)時(shí)的對(duì)象尤其明顯。并且如果能夠用單例解決的問(wèn)題經(jīng)歷使用單例解決,這樣能減輕運(yùn)行時(shí)的壓力。
    //1,對(duì)于一個(gè)對(duì)象倘若沒(méi)有成員變量,單例非常簡(jiǎn)單,不用去擔(dān)心多線程同時(shí)對(duì)成員變量修改而產(chǎn)生的線程安全問(wèn)題。
    //2,對(duì)于一個(gè)擁有成員變量的對(duì)象使用單例就需要考慮到線程安全問(wèn)題。多線程訪問(wèn)又可以分為下面兩個(gè)方面:
    // a:成員變量需要多線程同步,比如賬戶對(duì)象(ACCOUNT)中的成員變量余額(amount).amount成員變量需要在多線程的訪問(wèn)下保證各個(gè)線程保證絕對(duì)的同步,即無(wú)論什么時(shí)候線程內(nèi)的值都是一樣。
    // 我們可以通過(guò)加同步關(guān)鍵字synchronized,volatile來(lái)解決。
    // b,成員變量不需要線程同步,每個(gè)線程訪問(wèn)自己線程內(nèi)部的對(duì)象。比如一個(gè)服務(wù)類對(duì)數(shù)據(jù)庫(kù)的鏈接成員變量,每個(gè)線程分配一個(gè)連接即可。類似這種場(chǎng)景,我們最簡(jiǎn)單的方式是使用多例模式來(lái)解決。
    //單更好的方式是通過(guò)threadLocal來(lái)解決。

    //多例模式
    Thread thread = new Thread(){
        @Override
        public void run() {

            DBConnect dbConnect = new DBConnect();
            DemoService demoService = new DemoService();
            demoService.setConnect(dbConnect);
            demoService.doSomeThing();

        }
    };

    Thread thread2 = new Thread(){
        @Override
        public void run() {

            DBConnect dbConnect = new DBConnect();
            DemoService demoService = new DemoService();
            demoService.setConnect(dbConnect);
            demoService.doSomeThing();

        }
    };

    thread.start();
    thread2.start();

    // 單例模式改造
    // 由DemoService構(gòu)造器可以看出,構(gòu)造這個(gè)對(duì)象是非常耗時(shí)的。并且還不能使用單例模式,因?yàn)镈BConnect是不能多線程訪問(wèn)的。遇到這種情況那就使用ThreadLocal來(lái)改造吧。
    //如果能修改DemoService源碼,修改源碼即可。若不能修該源碼(比如DemoService是一個(gè)三方包)單DemoService不是final的,即可以通過(guò)繼承修改。

    DemoService demoService1 = new ThreadLocalDemoService();

    Thread threadA = new Thread(){
        @Override
        public void run() {

            demoService1.setConnect(new DBConnect());
            demoService1.doSomeThing();

        }
    };

    Thread threadB = new Thread(){
        @Override
        public void run() {

            demoService1.setConnect(new DBConnect());
            demoService1.doSomeThing();

        }
    };

    threadA.start();
    threadB.start();


}

static class DemoService{

    //這個(gè)對(duì)象不能線程同時(shí)訪問(wèn),應(yīng)該是一個(gè)線程就建立一個(gè)連接到數(shù)據(jù)庫(kù)。不同的線程不能使用同一個(gè)連接。
    DBConnect connect;

    public DemoService(){
        try {
            Thread.sleep(5l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
    public void setConnect(DBConnect connect){
        this.connect = connect;
    }


    public void doSomeThing(){
        connect.updateSomeData();
    }
}

//使用ThreadLocal改造成員變量,使其可以使其可以使用單例模式
static class ThreadLocalDemoService extends DemoService {

    ThreadLocal connectThreadLocal = new ThreadLocal<>();

    public ThreadLocalDemoService() {
        super();
    }



    public void doSomeThing(){
        connectThreadLocal.get().updateSomeData();
    }

    public void setConnect(DBConnect dbConnect){
        connectThreadLocal.set(dbConnect);
    }

}



class DBConnect {
    private String transactionName = Thread.currentThread().getName()+"的事務(wù)";

    public void updateSomeData(){
        System.out.println(transactionName + " update some data");
    }

}

其中DemoService中有個(gè)成語(yǔ)變量DBConnect connect,由于資源不能同時(shí)被連個(gè)線程使用,比如socket鏈接發(fā)送數(shù)據(jù),或者數(shù)據(jù)庫(kù)事務(wù),一個(gè)線程不能影響另外一個(gè)線程的事務(wù)。 這個(gè)時(shí)候我們沒(méi)有辦法只有對(duì)DemoService采用多例模式,單由因?yàn)镈emoService創(chuàng)建會(huì)耗費(fèi)大量時(shí)間。類似的例子很多,比如一個(gè)對(duì)象中,可能只有少數(shù)成員變量不能夠多線程訪問(wèn),大多數(shù)是能多線程訪問(wèn)的,這時(shí)為了一個(gè)成員變量去將單例改成多例也是非常糟糕的。這時(shí)若我們使用ThreadLocal就能夠完美解決。

備注

值得注意的是,ThreadLocal隨著當(dāng)前線程的銷毀而銷毀,如果程序中采用線程池,在上一次任務(wù)運(yùn)行完之后,記得清掉之前ThreadLocal數(shù)據(jù)。

引用

實(shí)際上學(xué)習(xí)和使用ThreadLocal之前,也百多過(guò)很多ThreadLocal相關(guān)的文章。最開(kāi)始是拜讀了學(xué)習(xí)Spring必學(xué)的Java基礎(chǔ)知識(shí)(6)----ThreadLocal@ITEYE這篇文章,才了解到ThreadLocal這個(gè)東西。最好為了詳細(xì)了解,看到了第二篇文章,并且之前看過(guò)的關(guān)于ThreadLocal的文章與這篇文章內(nèi)容基本上都一樣,都在講關(guān)于Threadloal為解決線程安全問(wèn)題提供了新思路,當(dāng)時(shí)被看得一頭霧水。最好看到第三篇的帖子。然后結(jié)合源代碼才對(duì)ThreadLocal的本質(zhì)很好的了解。如果你看完本篇文章還不是非常明白,可以詳細(xì)參閱第三篇引用,這個(gè)帖子的討論還是非常精彩的,能給你很多啟迪作用。需要說(shuō)明的是第二篇CSDN相關(guān)講解實(shí)際上是有問(wèn)題的。你可以看看文章下方的評(píng)論。

學(xué)習(xí)Spring必學(xué)的Java基礎(chǔ)知識(shí)(6)----ThreadLocal@ITEYE

徹底理解ThreadLocal@CSDN

正確理解ThreadLocal@ITEYE

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

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

相關(guān)文章

  • 阿里 2021 版最全 Java 并發(fā)編程筆記,看完我才懂了“內(nèi)卷”的真正意義

    摘要:純分享直接上干貨操作系統(tǒng)并發(fā)支持進(jìn)程管理內(nèi)存管理文件系統(tǒng)系統(tǒng)進(jìn)程間通信網(wǎng)絡(luò)通信阻塞隊(duì)列數(shù)組有界隊(duì)列鏈表無(wú)界隊(duì)列優(yōu)先級(jí)有限無(wú)界隊(duì)列延時(shí)無(wú)界隊(duì)列同步隊(duì)列隊(duì)列內(nèi)存模型線程通信機(jī)制內(nèi)存共享消息傳遞內(nèi)存模型順序一致性指令重排序原則內(nèi)存語(yǔ)義線程 純分享 , 直接上干貨! 操作系統(tǒng)并發(fā)支持 進(jìn)程管理內(nèi)存管...

    不知名網(wǎng)友 評(píng)論0 收藏0
  • 史上最全 Android 中高級(jí)工程師面試復(fù)習(xí)大綱

    摘要:找工作之前看了很多面試題,復(fù)習(xí)資料,但是發(fā)現(xiàn)純看面試題是不行的,因?yàn)榭勘车臇|西是記不牢的,需要知識(shí)成體系才可以,所以筆者整理了一份復(fù)習(xí)大綱,基本涵蓋了中高級(jí)工程師面試所必須知識(shí)點(diǎn),希望可以通過(guò)此文幫助一些想換工作的朋友更好的復(fù)習(xí),準(zhǔn)備面試。 概述 都說(shuō)金三銀四青銅五,這幾個(gè)月份是程序員最好的跳槽時(shí)間,筆者四月初也換了工作。找工作之前看了很多面試題,復(fù)習(xí)資料,但是發(fā)現(xiàn)純看面試題是不行的,因?yàn)榭?..

    chengjianhua 評(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 多線程(7): ThreadLocal 的應(yīng)用原理

    摘要:但是還有另外的功能看的后一半代碼作用就是掃描位置之后的數(shù)組直到某一個(gè)為的位置,清除每個(gè)為的,所以使用可以降低內(nèi)存泄漏的概率。 在涉及到多線程需要共享變量的時(shí)候,一般有兩種方法:其一就是使用互斥鎖,使得在每個(gè)時(shí)刻只能有一個(gè)線程訪問(wèn)該變量,好處就是便于編碼(直接使用 synchronized 關(guān)鍵字進(jìn)行同步訪問(wèn)),缺點(diǎn)在于這增加了線程間的競(jìng)爭(zhēng),降低了效率;其二就是使用本文要講的 Threa...

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

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

0條評(píng)論

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