自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深度解析Java Thread Locals工作原理

開發(fā) 前端
在Java中,線程本地變量的作用域是整個(gè)線程。這意味著這種變量可以從線程中的任何位置設(shè)置,并可以從同一線程的任何位置訪問。從一個(gè)線程設(shè)置的值對(duì)另一個(gè)線程是不可訪問的。

一、前言

在Java中,線程本地變量的作用域是整個(gè)線程。這意味著這種變量可以從線程中的任何位置設(shè)置,并可以從同一線程的任何位置訪問。從一個(gè)線程設(shè)置的值對(duì)另一個(gè)線程是不可訪問的。

我們應(yīng)該知道,Java中有兩種類型的線程本地類——ThreadLocal和InheritableThreadLocal。讓我們看看這兩者之間的區(qū)別。

二、ThreadLocal類

下面是一個(gè)如何聲明線程本地變量的示例。變量user是一個(gè)ThreadLocal變量,它保存一個(gè)User類型的變量(類或接口)。請(qǐng)注意,這里變量被聲明為public和static,以便user變量可以從代碼中的任何位置訪問。

// 聲明一個(gè)線程本地變量user
public static final ThreadLocal user 
                     = new ThreadLocal<>();

下面是我們?nèi)绾螢橐粋€(gè)線程設(shè)置和獲取user。該示例顯示user變量被設(shè)置為用戶對(duì)象bob。在同一線程中,如果我們調(diào)用get()方法,就會(huì)檢索到用戶bob。

// 設(shè)置調(diào)用線程的user值
user.set(new User("bob"));

// 獲取調(diào)用線程的user值
User requestUser = user.get();

請(qǐng)注意,即使user變量對(duì)整個(gè)代碼庫是可訪問的,但set(..)方法確保傳遞給它的用戶對(duì)象與calling線程相關(guān)聯(lián)。get()方法也會(huì)檢索與calling線程相關(guān)聯(lián)的用戶對(duì)象,這就是為什么當(dāng)在不同線程上調(diào)用get()方法時(shí),它不會(huì)檢索到bob而是其他用戶(或null)的原因。每個(gè)Java線程都與一個(gè)包含該線程所有設(shè)置的線程本地變量的ThreadLocal映射相關(guān)聯(lián)。

如果我們?cè)谖丛O(shè)置任何值的情況下調(diào)用get()方法,該方法將簡(jiǎn)單地返回null。

然而,你可以創(chuàng)建一個(gè)帶有Lambda Supplier的線程本地對(duì)象,它將返回一個(gè)初始的用戶對(duì)象。下面的示例顯示了一個(gè)Supplier,它返回一個(gè)名為anonymous的用戶。因此,如果在未設(shè)置值的情況下調(diào)用ThreadLocal上的get()方法,則會(huì)調(diào)用Supplier上的get()方法,并將該值設(shè)置為用戶的初始值。

// 聲明一個(gè)帶有Supplier的線程本地變量user
public static ThreadLocal user 
          = ThreadLocal.withInitial(
                () -> new User("anonymous"))

// 返回Anonymous
User requestUser = user.get();

你也可以通過簡(jiǎn)單地調(diào)用remove()方法來刪除之前設(shè)置的值,如下所示。

// 刪除調(diào)用線程的user值
user.remove();

該方法基本上會(huì)刪除與線程相關(guān)聯(lián)的用戶對(duì)象。更重要的是,其他線程不會(huì)受到此操作的影響。

如果我們以圖表形式來可視化線程本地變量,它看起來會(huì)像這樣。請(qǐng)注意,兩個(gè)線程的user變量指向的是不同的用戶對(duì)象。

圖片圖片

三、ThreadLocal和子線程

到目前為止,我們的討論主要集中在單個(gè)Java線程上。如果一個(gè)Java線程啟動(dòng)了一個(gè)新的子線程,子線程會(huì)自動(dòng)能夠訪問父線程中定義的線程本地變量嗎?

答案是否定的!子線程無法訪問父線程的線程本地變量,這是有充分理由的。如果能夠訪問,那么存儲(chǔ)在線程本地變量中的對(duì)象就必須為線程安全而編寫,因?yàn)槎鄠€(gè)線程能夠訪問同一個(gè)用戶對(duì)象。這是Java工程師做出的一個(gè)很好的默認(rèn)設(shè)計(jì)決策。

但是,在某些情況下,這種訪問是有用的。想象一個(gè)Web應(yīng)用程序的場(chǎng)景,許多用戶正在訪問應(yīng)用程序。一個(gè)單獨(dú)的Java線程與整個(gè)請(qǐng)求處理過程中的用戶相關(guān)聯(lián),你可以想象用戶對(duì)象存儲(chǔ)在線程的線程本地對(duì)象中(這是許多應(yīng)用服務(wù)器和框架如Spring Boot所做的)。但是,你可能希望生成的子線程也能訪問這些用戶信息。

對(duì)于這種場(chǎng)景,Java提供了另一個(gè)名為InheritableThreadLocal的類。

四、InheritableThreadLocal類

使用這個(gè)類的語法與ThreadLocal類基本相同。下面的示例顯示了InheritableThreadLocal類的相應(yīng)方法。

// 聲明一個(gè)可繼承的線程本地變量user
public static final InheritableThreadLocal user 
           = new InheritableThreadLocal<>();

// 設(shè)置調(diào)用線程的user值
user.set(new User("bob"));

// 獲取調(diào)用線程的user值
User requestUser = user.get();

// 刪除調(diào)用線程的user值
user.remove();

與Thread Local映射一樣,每個(gè)線程也有一個(gè)用于可繼承線程本地變量的映射。這里的關(guān)鍵區(qū)別是,當(dāng)創(chuàng)建子線程時(shí),子線程的可繼承線程本地映射會(huì)從父線程克隆。因此,可繼承線程本地變量對(duì)子線程也是可訪問的。

如果我們以圖表形式可視化可繼承線程本地變量,它看起來會(huì)像這樣??梢钥吹?,InheritableThreadLocal映射是從父線程克隆而來的。

圖片圖片

五、注意事項(xiàng)

正如上圖所清楚顯示的,可繼承線程本地變量所見到的優(yōu)勢(shì)也是一種缺點(diǎn)。默認(rèn)情況下,當(dāng)創(chuàng)建子線程時(shí),可繼承線程本地映射也會(huì)被克隆。但是你也可以看到,user指向父線程和子線程中相同的用戶對(duì)象。

這意味著用戶對(duì)象可以從多個(gè)線程訪問,因此需要以線程安全的方式編寫。換句話說,如果使用InheritableThreadLocal類,之前ThreadLocal類的線程安全性就會(huì)丟失。這對(duì)于你的設(shè)計(jì)可能是完全有效的。

然而,還有一種更安全的方法。我們可以在創(chuàng)建InheritableThreadLocal時(shí)指定一個(gè)childValue(..)方法。事實(shí)上,在下面的示例中,我們同時(shí)指定了一個(gè)初始值和一個(gè)子值。

public static final InheritableThreadLocal user 
                   = new InheritableThreadLocal<>() {

   @Override
   protected User initialValue() { 
      return new User("anonymous"); 
   }

   @Override
   protected User childValue(User parentValue) { 
      return new User(parentValue.getId()); 
   }
};

在這種更改下,當(dāng)Inheritable Thread Local映射被克隆時(shí),與子線程關(guān)聯(lián)的值將使用childValue(..)方法設(shè)置,該方法通過傳遞父線程的值來初始化每個(gè)Inheritable Thread Local。由于我們是從childValue(..)方法創(chuàng)建了一個(gè)新的對(duì)象,因此用戶對(duì)象不會(huì)在父線程和子線程之間共享。通過這一改變,我們恢復(fù)了線程安全性,同時(shí)也能以只讀的方式訪問用戶對(duì)象(通過有效地創(chuàng)建一個(gè)副本)。

同樣,如果我們以圖表形式可視化Inheritable Thread Locals,它看起來會(huì)像這樣。很明顯,現(xiàn)在用戶分別指向父線程和子線程中的不同用戶對(duì)象。

圖片圖片

希望這能讓你對(duì)Java Thread Local變量有一個(gè)較好的理解,以及它們?cè)趹?yīng)用程序中如何使用。

責(zé)任編輯:武曉燕 來源: Java學(xué)研大本營(yíng)
相關(guān)推薦

2012-06-29 13:54:11

Java內(nèi)存原型

2010-03-22 14:22:23

智能交換機(jī)

2024-02-05 13:52:30

?Thread對(duì)象強(qiáng)引用

2020-07-10 09:04:55

HTTPS瀏覽器網(wǎng)絡(luò)協(xié)議

2025-03-24 09:57:19

2010-08-30 11:08:53

DIV+CSS

2021-05-26 11:30:24

Java線程池代碼

2018-09-18 10:13:37

2010-09-26 10:09:25

dhcp relay工

2011-04-07 15:32:25

2024-08-09 08:12:35

深度學(xué)習(xí)VAEsGANs

2022-12-09 08:10:12

kubectl容器源碼

2011-06-16 15:28:31

正則表達(dá)式

2025-01-03 09:36:22

Nginx高并發(fā)進(jìn)程

2011-08-19 13:45:14

iPhone應(yīng)用iPhone OS數(shù)據(jù)

2023-06-13 09:53:59

智能汽車

2013-05-22 10:39:12

OpenFlowSDN軟件定義網(wǎng)絡(luò)

2021-07-13 10:00:01

ThreadJoin方法

2010-03-18 11:16:24

全光交換機(jī)

2015-11-04 09:23:17

JavaServlet工作原理
點(diǎn)贊
收藏

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