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

InheritableThreadLocal異步傳遞數(shù)據(jù)實(shí)現(xiàn)原理

開(kāi)發(fā) 前端
在Java中,一個(gè)Java線(xiàn)程就是一個(gè)操作系統(tǒng)線(xiàn)程,創(chuàng)建一個(gè)線(xiàn)程需要通過(guò)new Thread創(chuàng)建,由JVM為T(mén)hread綁定操作系統(tǒng)線(xiàn)程,即便是使用線(xiàn)程池,也需要通過(guò)new Thread創(chuàng)建線(xiàn)程。

由于上次主要分析如何解決異步獲取不到Session問(wèn)題,所以沒(méi)有展開(kāi)分析留下的那個(gè)思考題:使用InheritableThreadLocal傳遞Session,為什么說(shuō)使用線(xiàn)程池不一定能獲取到Session,而不是一定獲取不到?

在Java中,一個(gè)Java線(xiàn)程就是一個(gè)操作系統(tǒng)線(xiàn)程,創(chuàng)建一個(gè)線(xiàn)程需要通過(guò)new Thread創(chuàng)建,由JVM為T(mén)hread綁定操作系統(tǒng)線(xiàn)程,即便是使用線(xiàn)程池,也需要通過(guò)new Thread創(chuàng)建線(xiàn)程。

Thread類(lèi)有兩個(gè)ThreadLocal字段:

  1. public class Thread implements Runnable { 
  2.     ThreadLocal.ThreadLocalMap threadLocals = null
  3.     ThreadLocal.ThreadLocalMap inheritableThreadLocals = null

InheritableThreadLocal是ThreadLocal的子類(lèi),本質(zhì)上就是一個(gè)ThreadLocal。

在Thread類(lèi)中,threadLocals與inheritableThreadLocals都是線(xiàn)程對(duì)象私有的,只能通過(guò)當(dāng)前線(xiàn)程對(duì)象寫(xiě)入和獲取數(shù)據(jù),只是Thread會(huì)將寫(xiě)入inheritableThreadLocals的數(shù)據(jù)傳遞給子線(xiàn)程的inheritableThreadLocals。

當(dāng)我們往ThreadLocal或者InheritableThreadLocal寫(xiě)入數(shù)據(jù)時(shí),寫(xiě)入過(guò)程為:

  • 1、ThreadLocal或者InheritableThreadLocal先調(diào)用Thread#currentThread靜態(tài)方法獲取當(dāng)前線(xiàn)程的Thread對(duì)象;
  • 2、獲取Thread對(duì)象的threadLocals或者inheritableThreadLocals;
  • 3、將ThreadLocal或者InheritableThreadLocal對(duì)象作為key,將數(shù)據(jù)寫(xiě)入到當(dāng)前Thread對(duì)象的threadLocals或者inheritableThreadLocals字段中。

因此,Thread的threadLocals與inheritableThreadLocals的key是ThreadLocal或者InheritableThreadLocal實(shí)例,value是寫(xiě)入的數(shù)據(jù)。

關(guān)于threadLocals我在前面一篇《反向理解ThreadLocal,或許這樣更容易理解》已經(jīng)詳細(xì)介紹過(guò)了,本篇重點(diǎn)分析inheritableThreadLocals是如何傳遞給子線(xiàn)程的。

默認(rèn)情況下,當(dāng)我們使用new Thread()創(chuàng)建一個(gè)線(xiàn)程時(shí),在Thread的構(gòu)造方法中會(huì)通過(guò)Thread#currentThread獲取當(dāng)前線(xiàn)程,將當(dāng)前線(xiàn)程作為新創(chuàng)建線(xiàn)程的父線(xiàn)程,所以就有了父子線(xiàn)程關(guān)系。

無(wú)論使用哪個(gè)重載的構(gòu)造方法創(chuàng)建Thread,都會(huì)在構(gòu)造方法中調(diào)用init方法完成初始化為T(mén)hread字段賦值,而init方法中有這樣一段代碼:

  1. private void init(ThreadGroup g, Runnable target, String name
  2.                       long stackSize, AccessControlContext acc, 
  3.                       boolean inheritThreadLocals) { 
  4.         ...... 
  5.         if (inheritThreadLocals && parent.inheritableThreadLocals != null
  6.             this.inheritableThreadLocals = 
  7.                 ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); 
  8.        ...... 

在init方法中,由于inheritThreadLocals參數(shù)默認(rèn)為true,所以只要父線(xiàn)程的inheritableThreadLocals字段不為空,就copy一份父線(xiàn)程的inheritableThreadLocals給當(dāng)前創(chuàng)建的線(xiàn)程對(duì)象,這就實(shí)現(xiàn)了將父線(xiàn)程的inheritableThreadLocals存儲(chǔ)的數(shù)據(jù)傳遞給子線(xiàn)程。

使用InheritableThreadLocal我們不得不考慮的問(wèn)題:內(nèi)存泄漏。

ThreadLocal.ThreadLocalMap使用數(shù)組存儲(chǔ)元素,與HashMap不同,它通過(guò)開(kāi)放定址法解決hash沖突,不存在鏈表,通過(guò)動(dòng)態(tài)擴(kuò)容數(shù)組可無(wú)限存儲(chǔ)元素,數(shù)組元素的類(lèi)型為Entry。

當(dāng)我們往ThreadLocal.ThreadLocalMap寫(xiě)入一個(gè)key-value時(shí),ThreadLocalMap把key和value包裝成一個(gè)Entry,并通過(guò)key的hashcode值計(jì)算索引值,將Entry放到數(shù)組中。

ThreadLocal.ThreadLocalMap.Entry類(lèi)的源碼如下:

  1. static class Entry extends WeakReference<ThreadLocal<?>> { 
  2.    Object value; 
  3.    Entry(ThreadLocal<?> k, Object v) { 
  4.        super(k); 
  5.        value = v; 
  6.    } 

雖然key為弱引用的ThreadLocal,當(dāng)ThreadLocal釋放時(shí),Entry的key變?yōu)閚ull,但由于value還在,如果Thread不釋放,那么Entry也就不會(huì)被垃圾收集器回收。

但如果線(xiàn)程是臨時(shí)創(chuàng)建的,在方法中創(chuàng)建且沒(méi)有被其它地方引用,當(dāng)線(xiàn)程執(zhí)行完成時(shí)就會(huì)被JVM銷(xiāo)毀,在線(xiàn)程實(shí)際退出之前由JVM調(diào)用線(xiàn)程的exit方法給線(xiàn)程對(duì)象完成清理。exit方法部分源碼如下。

  1. private void exit() { 
  2.     ...... 
  3.     threadLocals = null
  4.     inheritableThreadLocals = null
  5.     ...... 

因此,只要Thread對(duì)象的exit方法被調(diào)用,就不會(huì)存在內(nèi)存泄漏問(wèn)題。只要線(xiàn)程用完就銷(xiāo)毀,那么使用InheritableThreadLocal,在子線(xiàn)程中不需要調(diào)用InheritableThreadLocal的remove方法也不會(huì)存在內(nèi)存泄漏的可能。

比如我們?cè)陧?xiàng)目中使用InheritableThreadLocal實(shí)現(xiàn)將Session傳遞給子線(xiàn)程:

  1. @GetMapping("/test"
  2. public SsoUser test() { 
  3.     // 獲取登錄用戶(hù) 
  4.     SsoUser ssoUser = SsoUserManager.curLoggedUser(); 
  5.     System.out.println(ssoUser.getUserCode()); 
  6.     // 支持子線(xiàn)程傳遞 
  7.     new Thread(() -> { 
  8.         try { 
  9.             Thread.sleep(100); 
  10.             SsoUser ssoUser2 = SsoUserManager.curLoggedUser(); 
  11.             System.out.println(ssoUser2.getUserCode()); 
  12.         } catch (InterruptedException e) { 
  13.         } 
  14.     }).start(); 
  15.     return ssoUser; 

在此案例中,由于子線(xiàn)程只是臨時(shí)創(chuàng)建的,所以我們不需要在子線(xiàn)程中調(diào)用InheritableThreadLocal的remove方法,只需要在父線(xiàn)程調(diào)用一次remove方法,因?yàn)閠omcat的work線(xiàn)程是不會(huì)在一次請(qǐng)求結(jié)束后就銷(xiāo)毀的。

現(xiàn)在我們已經(jīng)知道了InheritableThreadLocal是如何實(shí)現(xiàn)將數(shù)據(jù)傳遞給子線(xiàn)程的,思考題的答案也就有了一半:由于InheritableThreadLocal只能將線(xiàn)程上下文傳遞給當(dāng)前線(xiàn)程創(chuàng)建的子線(xiàn)程,所以只有線(xiàn)程池中的線(xiàn)程是由當(dāng)前線(xiàn)程創(chuàng)建的才能夠傳遞。

但要知道另一半答案我們還需要從線(xiàn)程池中尋找。

使用不同參數(shù)構(gòu)建的線(xiàn)程池不同,常見(jiàn)的有單線(xiàn)程的線(xiàn)程池、只有固定數(shù)量核心線(xiàn)程的線(xiàn)程池、有固定數(shù)量核心線(xiàn)程和非核心線(xiàn)程的線(xiàn)程池、只有非核心線(xiàn)程的線(xiàn)程池。

線(xiàn)程池的幾個(gè)構(gòu)造參數(shù)說(shuō)明如下:

  • corePoolSize:核心線(xiàn)程數(shù),不會(huì)被釋放的線(xiàn)程數(shù)量(設(shè)置allowCoreThreadTimeOut為ture時(shí)例外);
  • maximumPoolSize:線(xiàn)程池的最大線(xiàn)程數(shù),等于核心線(xiàn)程與非核心線(xiàn)程的數(shù)量總和;
  • keepAliveTime:非核心線(xiàn)程最大空閑等待時(shí)間,在指定空閑時(shí)間后如果還沒(méi)有任務(wù)則釋放該線(xiàn)程;
  • workQueue:任務(wù)隊(duì)列,當(dāng)核心線(xiàn)程數(shù)用完時(shí),任務(wù)被放入隊(duì)列。

一、線(xiàn)程池是臨時(shí)線(xiàn)程池

如果線(xiàn)程池是在當(dāng)前線(xiàn)程創(chuàng)建的,且任務(wù)都是由當(dāng)前線(xiàn)程提交的,線(xiàn)程池用完就消毀了,那么不管是哪種線(xiàn)程池,池中的線(xiàn)程都是由當(dāng)前線(xiàn)程所創(chuàng)建,在這種場(chǎng)景下,InheritableThreadLocal能夠?qū)ontext傳給給線(xiàn)程池中的任一線(xiàn)程。

二、線(xiàn)程池是全局線(xiàn)程池

如果線(xiàn)程池是全局線(xiàn)程池:

  • 沒(méi)有核心線(xiàn)程且非核心線(xiàn)程的keepAliveTime等于0:線(xiàn)程都是用到才創(chuàng)建,且由于keepAliveTime等于0,線(xiàn)程用完可能就釋放了,在這種場(chǎng)景下,相當(dāng)于是由當(dāng)前線(xiàn)程創(chuàng)建子線(xiàn)程執(zhí)行任務(wù),因此能夠?qū)崿F(xiàn)透?jìng)?
  • 沒(méi)有非核心線(xiàn)程:前(核心線(xiàn)程數(shù))個(gè)任務(wù)的提交都會(huì)創(chuàng)建線(xiàn)程,也都是由當(dāng)前線(xiàn)程創(chuàng)建,所以只有這幾個(gè)任務(wù)的執(zhí)行是能夠正常獲取父線(xiàn)程寫(xiě)入InheritableThreadLocal的數(shù)據(jù)的,后面提交的任務(wù)就不知道會(huì)被哪個(gè)核心線(xiàn)程拉取執(zhí)行了;
  • 其它:....

因此,如果線(xiàn)程池是全局線(xiàn)程池,那么無(wú)論是哪個(gè)情況,都不建議使

本文轉(zhuǎn)載自微信公眾號(hào)「Java藝術(shù)」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java藝術(shù)公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: Java藝術(shù)
相關(guān)推薦

2024-07-09 08:35:09

2024-11-25 13:49:00

2023-10-07 08:26:40

多線(xiàn)程數(shù)據(jù)傳遞數(shù)據(jù)共享

2013-10-18 15:49:15

微軟大數(shù)據(jù)微軟

2023-01-04 08:38:43

Spring異步線(xiàn)程

2015-05-05 09:50:45

大數(shù)據(jù)實(shí)現(xiàn)塊數(shù)據(jù)

2010-01-12 14:02:14

VB.NET數(shù)據(jù)實(shí)體層

2014-01-22 11:22:44

華為HANA一體機(jī)FusionCube大數(shù)據(jù)分析

2024-07-03 08:02:19

MySQL數(shù)據(jù)搜索

2010-01-13 16:52:03

VB.NET導(dǎo)出數(shù)據(jù)

2025-01-10 10:44:52

2016-09-13 22:46:41

大數(shù)據(jù)

2020-09-07 11:14:02

Vue異步更新

2011-07-22 09:51:51

iPhone FROM 表單

2017-03-13 09:48:26

pysparkhive數(shù)據(jù)

2014-09-16 13:33:50

大數(shù)據(jù)

2020-10-16 11:41:07

攻擊

2022-11-30 18:38:50

數(shù)據(jù)血緣DataLeap

2020-06-01 15:13:41

騰訊云圖數(shù)據(jù)庫(kù)

2015-07-13 14:27:48

大數(shù)據(jù)Cloudera價(jià)值
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 国产精品久久久久av | 精品日韩一区二区三区 | 日韩影音| 国产精品毛片一区二区三区 | 精久久久 | 国产亚洲精品久久yy50 | 国产精品成人一区二区 | 国产高清久久 | 美女视频一区 | 久久亚洲国产精品 | 97国产一区二区 | 久久99久久99 | 91在线视频观看免费 | 欧美精品久久久久久久久久 | 日本在线精品视频 | 天天干.com | 亚洲日本欧美日韩高观看 | 亚洲欧美日韩成人在线 | 在线观看黄色 | 精品亚洲视频在线 | 国产精品免费一区二区三区四区 | 亚洲男人的天堂网站 | 欧美性受 | 在线视频一区二区 | 日本一区二区三区视频在线 | 99色视频| a欧美| 日韩在线视频精品 | 九九久久精品视频 | 六月婷婷久久 | 亚洲444kkkk在线观看最新 | av天天看| 99riav国产一区二区三区 | 久久亚洲一区二区三区四区 | 玖玖操 | 亚洲高清视频在线 | 成人免费看黄 | av黄色在线观看 | 精品成人av | 七七婷婷婷婷精品国产 | 日韩国产高清在线观看 |