成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

InheritableThreadLocal 是如何實(shí)現(xiàn)的父子線程局部變量的傳遞

開發(fā)
本文我們通過一個(gè)示例,驗(yàn)證了父子線程間可以通過ThreadLocal進(jìn)行傳遞,測試了不同方式初始化ThreadLocal,并對比了new Thread()與線程池啟動的區(qū)別。

今天聊一聊并發(fā)編程中經(jīng)常遇到也是面試時(shí)容易被為難的一個(gè)題目,線程間局部變量的傳遞問題。

相信對并發(fā)編程有一定了解的同學(xué)已經(jīng)想到了大名鼎鼎的 ThreadLocal 了,是的,線程內(nèi)部就是通過 inheritableThreadLocals 實(shí)現(xiàn)了父子線程間局部變量的傳遞。

JDK 8

一、父子線程間局部變量參數(shù)傳遞的方式 ThreadLocal

首先我們寫看一段代碼:


public class ThreadLocalTest implements Runnable{
    private static final InheritableThreadLocal<String> MAIN_THREAD_LOCAL = new InheritableThreadLocal<>();
    @SneakyThrows
    @Override
    public void run() {
        System.out.println("threadlocal 默認(rèn)值:"+ThreadLocalTest.MAIN_THREAD_LOCAL.get());
        MAIN_THREAD_LOCAL.set("child thread value :"+Thread.currentThread().getName());
        System.out.println("threadlocal 設(shè)置子線程值之后:"+ThreadLocalTest.MAIN_THREAD_LOCAL.get());
    }
    public String get(){
        return MAIN_THREAD_LOCAL.get();
    }
    public void clean(){
        MAIN_THREAD_LOCAL.remove();
    }
    public static void main(String[] args) {
        ThreadLocalTest threadLocalTest = new ThreadLocalTest();
        MAIN_THREAD_LOCAL.set("父線程的值 set 111");
        System.out.println("啟動:"+threadLocalTest.get());
        for (int i = 0; i < 3; i++) {
            new Thread(threadLocalTest).start();
//            ThreadUtil.execAsync(threadLocalTest);
        }
        System.out.println("結(jié)束:"+threadLocalTest.get());
    }
}

在上面的這段代碼中,我們就做了三個(gè)事情:

  • 設(shè)置父線程中定義ThreadLocal的值。
  • 在子線程中打印父線程中ThreadLocal的值。
  • 啟動多個(gè)子線程

大家可以先猜一下這段代碼的運(yùn)行結(jié)果。

二、子線程可以繼承父線程局部變量的值嗎

首先我們先說下答案,是可以繼承的。上面代碼的執(zhí)行結(jié)果如下:

啟動:父線程的值 set 111
結(jié)束:父線程的值 set 111
threadlocal 默認(rèn)值:父線程的值 set 111
threadlocal 設(shè)置子線程值之后:child thread value :Thread-1
threadlocal 默認(rèn)值:父線程的值 set 111
threadlocal 默認(rèn)值:父線程的值 set 111
threadlocal 設(shè)置子線程值之后:child thread value :Thread-2
threadlocal 設(shè)置子線程值之后:child thread value :Thread-0

在上面的代碼中,我們的子線程優(yōu)先打印了父線程中ThreadLocal的值,然后重新設(shè)置該值,再次讀取。得出結(jié)論就是子線程可以通過ThreadLocal繼承父線程的值,并且子線程自己內(nèi)容再次重新設(shè)置不影響父線程的值。

三、父子線程局部變量傳值的原理

難道一句簡單的ThreadLocal就可以讓我們對這個(gè)問題停止探索嗎?那么線程內(nèi)部是如何通過ThreadLocal進(jìn)行傳值的呢?

1.new thread

在上面代碼中,啟動子線程的方式是new Thread(threadLocalTest).start();,所以秘密一定就在這一行代碼里面。源碼之下無秘密,我們一起來看下。

首先進(jìn)入new Thread()的內(nèi)部:

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

通過上面兩個(gè)方法調(diào)用,最終進(jìn)入到下面這個(gè)方法中:

 private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {}

init方法有個(gè)參數(shù)inheritThreadLocals,boolean類型的,如果為true,且可繼承的線程局部變量不為空就繼承。

現(xiàn)在我們只需要順著inheritThreadLocals這個(gè)參數(shù)去找就可以了,在Thread的418行,有這樣一行代碼。(代碼行數(shù)可能因版本而位置不同)

可以看到是直接對當(dāng)前線程的inheritableThreadLocals直接進(jìn)行的賦值操作,而值是通過ThreadLocal.createInheritedMap獲取的,下面我們看下這個(gè)createInheritedMap方法做了哪些操作?

createInheritedMap方法是ThredLocal內(nèi)部的方法,接收傳遞父線程的ThreadLocalMap為參數(shù),該方法只做了一個(gè)事情,就是new了一個(gè)新的ThreadLocalMap。

跟進(jìn)到new ThreadLocalMap(parentMap)方法內(nèi)部,其實(shí)是把傳進(jìn)的值,一個(gè)個(gè)的遍歷進(jìn)行賦值到當(dāng)前線程中。

對于圖中標(biāo)記的第二個(gè)地方,childValue調(diào)用的是InheritableThreadLocal#childValue,該方法內(nèi)也只做了一件事,就是返回傳進(jìn)來的值。

(1) 小結(jié)

父子線程之所以能傳參,是因?yàn)槲覀兪褂昧薎nheritableThreadLocal,這樣在new Thread()時(shí),就會進(jìn)入到給子線程賦值父線程inheritableThreadLocals的邏輯中去。

(2) 擴(kuò)展

有的同學(xué)會說了,我用 ThreadLocal.withInitial創(chuàng)建的,怎么走到線程的if (inheritThreadLocals && parent.inheritableThreadLocals != null)判斷時(shí),沒有進(jìn)去呢,上面不是說是在這判斷然后對子線程進(jìn)行賦值的嗎?

在這簡單說一下哈,大家在寫代碼時(shí),或者再用第三方框架時(shí),源碼中的注釋一定要看仔細(xì),很多細(xì)節(jié)都在注釋中標(biāo)注清楚了。

    public static ThreadLocal<String> MAIN_THREAD_LOCAL = ThreadLocal.withInitial(() -> "父線程的值 withInitial 111");

在上面的代碼中,我們進(jìn)行了ThreadLocal的初始化賦值,然后看下withInitial方法。

所以是當(dāng)調(diào)用get方法時(shí),才會觸發(fā)賦值的操作,那么我們看下get方法。

如果當(dāng)前線程的局部變量沒有值,返回初始化方法初始的值。

所以對于我們來說就是SuppliedThreadLocal#initialValue返回的值。

2.線程池

剛才我們是通過new Thread()啟動的子線程,可是工作中基本都是通過線程池的方式執(zhí)行任務(wù)的啊,那還生效嗎?

答案是生效。

我們使用hutool工具中的ThreadUtil.execAsync(threadLocalTest);進(jìn)行測試。

直接說結(jié)論,感興趣的同學(xué)可以自行修改一下代碼中的子線程啟動方式。

先畫個(gè)流程圖,大家可以跟著代碼走一下。

當(dāng)使用線程池時(shí),底層原理還是線程池中放入任務(wù)的邏輯,當(dāng)放入線程池之后,會在AbstractExecutorService#submit()方法中執(zhí)行execute方法,最終執(zhí)行在ThreadPoolExecutor#execute(),在這里,就是把任務(wù)丟入線程池工作的邏輯,其中有個(gè)方法addWorker,該方法中有一行new Worker(),而在該Worker方法的內(nèi)部,其實(shí)就是new Thread(),到了這,就與上面所說的一樣了,到了判斷inheritableThreadLocals的時(shí)候了。

四、如何解決內(nèi)存泄漏

使用ThreadLocal的應(yīng)用場景有很多,父子線程傳參數(shù)的場景也有不少,但是有一個(gè)很關(guān)鍵的點(diǎn)內(nèi)存溢出是需要重視的。解決ThreadLocal內(nèi)存溢出的方式也很簡單,就是在使用完成之后調(diào)用一下remove。

對于上面的代碼示例,就是調(diào)用我們的clean方法。

public void clean(){

 MAIN_THREAD_LOCAL.remove();

}

remove的代碼如下,取值不為null時(shí),執(zhí)行刪除邏輯。

五、總結(jié)

我們通過一個(gè)示例,驗(yàn)證了父子線程間可以通過ThreadLocal進(jìn)行傳遞,測試了不同方式初始化ThreadLocal,并對比了new Thread()與線程池啟動的區(qū)別。

其實(shí)殊途同歸,線程池最后調(diào)用的還是Thread里面的方法。唯一需要注意的就是通過ThreadLocal.withInitial初始化是在get時(shí)賦值的,不過這個(gè)應(yīng)該也不重要,了解一下就好,應(yīng)該也沒有面試官會這么摳這個(gè)問題吧。

責(zé)任編輯:趙寧寧 來源: 醉魚Java
相關(guān)推薦

2009-09-22 17:21:24

線程局部變量

2012-07-11 23:10:49

SQL Server數(shù)據(jù)庫

2020-10-26 07:07:50

線程安全框架

2010-03-15 09:32:56

Python函數(shù)

2020-11-11 21:26:48

函數(shù)變量

2021-02-09 09:51:58

異步傳遞數(shù)據(jù)

2009-09-17 13:05:38

Linq局部變量類型

2024-05-29 08:49:22

Python全局變量局部變量

2015-01-07 14:41:32

Android全局變量局部變量

2019-08-22 15:06:56

線程Java透傳

2009-08-26 16:37:07

C#迭代器局部變量

2017-02-08 12:28:37

Android變量總結(jié)

2024-10-28 12:06:09

2018-05-14 09:15:24

Python變量函數(shù)

2010-10-14 09:34:34

JVM局部變量

2009-09-11 10:07:05

Linq隱式類型化局部

2009-10-12 14:13:00

VB.NET使用局部變

2009-12-15 10:48:54

Ruby局部變量

2011-11-23 10:59:18

Javafinal

2010-01-08 15:22:22

VB.NET局部變量
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 国产我和子的乱视频网站 | 国产亚洲精品一区二区三区 | 91久久婷婷 | 中文字幕免费中文 | 日韩三级电影一区二区 | 黄网站免费观看 | 中文亚洲视频 | 欧美激情在线一区二区三区 | 日韩欧美视频 | 一区二区在线不卡 | 国产一区二区三区色淫影院 | 日本精品在线观看 | 久草在线青青草 | 97人人超碰 | tube国产 | 青青草一区二区三区 | 中文字幕精品一区 | 伊人成人免费视频 | 国产一二三区在线 | 亚洲精品一区二区三区四区高清 | 日韩亚洲视频在线 | 久久不卡 | 一区二区三区在线播放 | 成人av一区| 国产黄色大片 | 在线观看黄视频 | 日韩精品| 日韩色视频 | 成人在线视频网址 | 黄色三级免费网站 | 精品一区二区三区免费毛片 | 亚洲毛片 | 午夜av一区二区 | 欧美视频三区 | 高清欧美性猛交 | 国产中文一区二区三区 | 青青久草 | 欧美中文一区 | 精品在线免费看 | 久久精品色欧美aⅴ一区二区 | 天天插天天射天天干 |