偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

你的也是我的。3例ko多線程,局部變量透?jìng)?/h1>

開發(fā) 后端
java中的threadlocal,是綁定在線程上的。你在一個(gè)線程中set的值,在另外一個(gè)線程是拿不到的。如果在threadlocal的平行線程中,創(chuàng)建了新的子線程,那么這里面的值是無(wú)法傳遞、共享的(先想清楚為什么再往下看)。這就是透?jìng)鲉?wèn)題。

java中的threadlocal,是綁定在線程上的。你在一個(gè)線程中set的值,在另外一個(gè)線程是拿不到的。如果在threadlocal的平行線程中,創(chuàng)建了新的子線程,那么這里面的值是無(wú)法傳遞、共享的(先想清楚為什么再往下看)。這就是透?jìng)鲉?wèn)題。

[[274587]]

值在線程之間的透?jìng)?,你可以認(rèn)為是一個(gè)bug,這些問(wèn)題一般會(huì)比較隱蔽,但問(wèn)題暴露的時(shí)候脾氣卻比較火爆,讓人手忙腳亂,懷疑人生。

作為代碼的掌舵者,我們必然不能忍受這種問(wèn)題的蹂躪。本篇文章適合細(xì)看,我們拿出3個(gè)例子,通過(guò)編碼手段說(shuō)明解決此類bug的通用方式,希望能達(dá)到舉一反三的效果。對(duì)于搞基礎(chǔ)架構(gòu)的同學(xué),是必備知識(shí)點(diǎn)。

1、普通線程的ThreadLocal透?jìng)鲉?wèn)題

2、sl4j MDC組件中ThreadLocal透?jìng)鲉?wèn)題

3、Hystrix組件的透?jìng)鲉?wèn)題

由于涉及代碼比較多,xjjdog將這三個(gè)例子的代碼,放在了github上,想深入研究,可以下載下來(lái)debug一下。

  1. https://github.com/xjjdog/example-pass-through 

一、問(wèn)題簡(jiǎn)單演示

為了有個(gè)比較直觀的認(rèn)識(shí),下面展示一段異常代碼。

以上代碼在主線程設(shè)置了一個(gè)簡(jiǎn)單的threadlocal變量,然后在自線程中想要取出它的值。執(zhí)行后發(fā)現(xiàn),程序的輸出是:null。

程序的輸出和我們的期望產(chǎn)生了明顯的差異。其實(shí),將ThreadLocal 換成InheritableThreadLocal 就ok了。不要高興太早,對(duì)于使用線程池的情況,由于會(huì)緩存線程,線程是緩存起來(lái)反復(fù)使用的。這時(shí)父子線程關(guān)系的上下文傳遞,已經(jīng)沒有意義。

二、解決線程池透?jìng)鲉?wèn)題

所以,線程池InheritableThreadLocal進(jìn)行提交,獲取的值,有可能是前一個(gè)任務(wù)執(zhí)行后留下的,是錯(cuò)誤的。使用只有在任務(wù)執(zhí)行的時(shí)候進(jìn)行傳遞,才是正常的功能。

上面的問(wèn)題,transmittable-thread-local項(xiàng)目,已經(jīng)很好的解決,并提供了java-agent的方式支持。

我們這里從最小集合的源碼層面,來(lái)看一下其中的內(nèi)容。首先,我們看一下ThreadLocal的結(jié)構(gòu)。

ThreadLocal其實(shí)是作為一個(gè)Map中的key而存在的,這個(gè)Map就是ThreadLocalMap,它以私有變量的形式,存在于Thread類中。拿上圖為例,如果我創(chuàng)建了一個(gè)ThreadLocal,然后調(diào)用set方法,它會(huì)首先找到當(dāng)前的thread,然后找到threadLocals,最后把自己作為key,存放在這個(gè)map里。

  1. hread t = Thread.currentThread(); 
  2. ThreadLocalMap map = getMap(t); 
  3. map.set(this, value); 

要能夠完成多線程的協(xié)調(diào)工作,必須提供全套的多線程工具。包括但不限于:

1、定義注解,以及被注解修飾的ThreadLocal類

定義新的ThreadLocal類,以便在賦值的時(shí)候,能夠根據(jù)注解進(jìn)行攔截和過(guò)濾。這就要求,在定義ThreadLocal的時(shí)候,要使用我們提供的ThreadLocal類,而不是jdk提供的那兩個(gè)。

2、進(jìn)行父子線程之間的數(shù)據(jù)拷貝

在線程池提交任務(wù)之前,我們需要有個(gè)地方,將父進(jìn)程的ThreadLocal內(nèi)容,暫存一下。

由于很多變量都是private的,需要根據(jù)反射進(jìn)行操作。根據(jù)上面提供的ThreadLocal類的結(jié)構(gòu),我們需要直接操作其中的變量table(這也是為什么jdk不能隨便改變變量名的原因)。

將父線程相關(guān)的變量暫存之后,就可以在使用的時(shí)候,通過(guò)主動(dòng)設(shè)值和清理,完成變量拷貝。

3、提供專用的Callable或者Runnable

那么這些數(shù)據(jù)是如何組裝起來(lái)的呢?還是靠我們的任務(wù)載體類。

線程池提交線程,一般是通過(guò)Callable或者Runnable,以Runnable為例,我們看一下這個(gè)調(diào)用關(guān)系。

以下類采用了委托模式。

這樣,只要在提交任務(wù)的時(shí)候,使用了我們自定義的Runnable;同時(shí),使用了自定義的ThreadLocal,就能夠正常完成透?jìng)鳌?/p>

三、解決MDC透?jìng)鲉?wèn)題

sl4j MDC機(jī)制非常好,通常用于保存線程本地的“診斷數(shù)據(jù)”然后有日志組件打印,其內(nèi)部時(shí)基于threadLocal實(shí)現(xiàn);不過(guò)這就有一些問(wèn)題,主線程中設(shè)置的MDC數(shù)據(jù),在其子線程(多線程池)中是無(wú)法獲取的,下面就來(lái)介紹如何解決這個(gè)問(wèn)題。

!MDC ( Mapped Diagnostic Contexts ),它是一個(gè)線程安全的存放診斷日志的容器。通常,會(huì)在處理請(qǐng)求前將請(qǐng)求的唯一標(biāo)示放到MDC容器中,比如sessionId。這個(gè)唯一標(biāo)示會(huì)隨著日志一起輸出。配置文件可以使用占位符進(jìn)行變量替換。

類似于上面介紹的方式,我們需要提供專用的Callable和Runnable。另外,為了能夠同時(shí)支持MDC和普通線程,這兩個(gè)類采用裝飾器模式,進(jìn)行功能追加。就單個(gè)類來(lái)說(shuō),對(duì)外的展現(xiàn)依然是委托模式。

同樣的思路,同樣的模式。不一樣的是,父線程的信息暫存,我們直接使用MDC的內(nèi)部方法,并在任務(wù)的執(zhí)行前后,進(jìn)行相應(yīng)操作。

四、解決Hystrix透?jìng)鲉?wèn)題

同樣的問(wèn)題,在Netflix公司的熔斷組件Hystrix中,依然存在。Hystrix線程池模式下,透?jìng)鱐hreadLocal需要進(jìn)行改造,它本身是無(wú)法完成這個(gè)功能的。

但是Hystrix策略無(wú)法簡(jiǎn)單通過(guò)yml文件方式配置。我們參考Spring Cloud中對(duì)此策略的擴(kuò)展方式,開發(fā)自己的策略。需要繼承HystrixConcurrentStrategy。

構(gòu)造代碼還是較長(zhǎng)的,可以查看github項(xiàng)目。但有一個(gè)地方需要說(shuō)明。

我們使用裝飾器模式,對(duì)代碼進(jìn)行了層層嵌套,同時(shí)將多線程透?jìng)鞴δ?、MDC傳遞功能給追加了進(jìn)來(lái)。這樣,我們的這個(gè)類,就同時(shí)在以上三個(gè)環(huán)境中擁有了透?jìng)鞴δ堋?/p>

End

同樣的思路,可以用在其他組件上。比如我們?cè)诙嗥{(diào)用鏈的文章里,提到的trace信息在多線程環(huán)境下的傳遞。

一般就是在當(dāng)前線程暫存數(shù)據(jù),然后在提交任務(wù)時(shí)進(jìn)行包裝。值得注意的是,這種方式侵入性還是比較大的,適合封裝在通用的基礎(chǔ)工具包中。你要是在業(yè)務(wù)中這么用,大概率會(huì)被罵死。

那可如何是好。

ThreadLocal會(huì)引發(fā)很多棘手的bug,造成代碼污染。在使用之前,一定要確保你確實(shí)需要使用它。比如你在SimpleDateFormat類上用了線程局部變量,可以將它替換成DateTimeFormatter。

我們不善于解決問(wèn)題,我們只善于解決容易出問(wèn)題的類。

作者簡(jiǎn)介:小姐姐味道 (xjjdog),一個(gè)不允許程序員走彎路的公眾號(hào)。聚焦基礎(chǔ)架構(gòu)和Linux。十年架構(gòu),日百億流量,與你探討高并發(fā)世界,給你不一樣的味道。我的個(gè)人微信xjjdog0,歡迎添加好友,進(jìn)一步交流。

 

責(zé)任編輯:武曉燕 來(lái)源: 小姐姐味道
相關(guān)推薦

2009-09-22 17:21:24

線程局部變量

2024-07-09 08:35:09

2010-03-15 09:32:56

Python函數(shù)

2020-11-11 21:26:48

函數(shù)變量

2009-09-17 13:05:38

Linq局部變量類型

2024-05-29 08:49:22

Python全局變量局部變量

2015-01-07 14:41:32

Android全局變量局部變量

2020-10-26 07:07:50

線程安全框架

2009-08-26 16:37:07

C#迭代器局部變量

2017-02-08 12:28:37

Android變量總結(jié)

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使用局部變

2012-07-11 23:10:49

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

2009-12-15 10:48:54

Ruby局部變量

2011-11-23 10:59:18

Javafinal

2010-01-08 15:22:22

VB.NET局部變量

2009-08-27 10:08:36

C#隱含類型局部變量

2021-09-28 07:12:09

函數(shù)內(nèi)存
點(diǎn)贊
收藏

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