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

反問(wèn)面試官三個(gè) ThreadLocal 的問(wèn)題

開(kāi)發(fā)
ThreadLocal? 是并發(fā)編程中的強(qiáng)大工具,能夠?yàn)槊總€(gè)線程提供獨(dú)立的變量副本,避免線程安全問(wèn)題。

ThreadLocal,一個(gè)Java人面試?yán)@不開(kāi)的話題,我也很奇怪為什么那些面試官很喜歡問(wèn)這個(gè),也不知道他們自己有沒(méi)有搞清楚。

接下來(lái),我想先說(shuō)說(shuō)ThreadLocal的用法和使用場(chǎng)景,然后反問(wèn)面試官3個(gè)關(guān)于ThreadLocal的話題。

使用方法和場(chǎng)景

一句話總結(jié):ThreadLocal是給每個(gè)線程準(zhǔn)備一份“獨(dú)立的小空間”,它讓每個(gè)線程都擁有自己獨(dú)立的變量副本。在多個(gè)線程并發(fā)訪問(wèn)時(shí),不用擔(dān)心變量之間的沖突問(wèn)題,避免了多線程之間的數(shù)據(jù)共享風(fēng)險(xiǎn)。

1.使用場(chǎng)景

ThreadLocal的使用場(chǎng)景主要在多線程環(huán)境中,能夠?yàn)槊總€(gè)線程提供獨(dú)立的變量副本。比如:

  • 用戶上下文信息:比如,在Web應(yīng)用中,每個(gè)請(qǐng)求可能由不同的線程處理,在處理用戶請(qǐng)求時(shí)為每個(gè)線程維護(hù)獨(dú)立的用戶信息。
  • 數(shù)據(jù)庫(kù)連接管理:比如,在多線程環(huán)境下,每個(gè)線程需要有自己獨(dú)立的數(shù)據(jù)庫(kù)連接。
  • 事務(wù)管理:比如,在處理事務(wù)時(shí),每個(gè)線程可能需要有自己的事務(wù)上下文,確保線程安全的事務(wù)操作。
  • 數(shù)據(jù)傳遞:比如,同一個(gè)線程,在不同的方法之間傳遞數(shù)據(jù),但又不想使用方法參數(shù)去傳遞,就可以使用ThreadLocal。像我們常用的日志跟蹤場(chǎng)景,跟蹤的ID會(huì)存在ThreadLocal中貫穿整個(gè)鏈條。

總之有2個(gè)場(chǎng)景:

  • 在多線程場(chǎng)景下,每個(gè)線程需要獨(dú)立管理變量的場(chǎng)景。
  • 某個(gè)線程想在整條鏈路上共享獨(dú)立變量的場(chǎng)景。

2.使用方法

使用時(shí),記住3條核心原則:

  • 每個(gè)線程都有一份獨(dú)立的數(shù)據(jù)。
  • 線程內(nèi)部使用的是ThreadLocalMap來(lái)保存數(shù)據(jù),Key就是ThreadLocal對(duì)象。
  • 使用完畢后,記得調(diào)用remove方法,防止內(nèi)存溢出。

代碼示例

獨(dú)立保存變量的示例:

public class ThreadLocal4Independent {

    private static ThreadLocal<Integer> threadLocalVar = new ThreadLocal<>();

    public static void main(String[] args) {
        Runnable task = () -> {
            int num = (int) (Math.random() * 100);
            threadLocalVar.set(num);
            System.out.println("線程:" + Thread.currentThread().getName() + "的值:" + threadLocalVar.get());
            threadLocalVar.remove();
        };

        new Thread(task, "1").start();
        new Thread(task, "2").start();
    }

}

傳遞參數(shù)的示例:

public class ThreadLocal4DataPass {
    // 使用ThreadLocal來(lái)存儲(chǔ)需要在多個(gè)方法間傳遞的數(shù)據(jù)
    private static final ThreadLocal<String> threadLocalData = new ThreadLocal<>();

    public static void main(String[] args) {
        // 在主線程中設(shè)置數(shù)據(jù)
        threadLocalData.set("ThreadLocal");

        // 在主線程中調(diào)用不同的方法
        method1();
        method2();
        
        // 清除ThreadLocal變量,防止內(nèi)存泄露
        threadLocalData.remove();
    }

    private static void method1() {
        // 在method1中獲取數(shù)據(jù)并打印
        String data = threadLocalData.get();
        System.out.println("方法1拿到的數(shù)據(jù)是:" + data);
    }

    private static void method2() {
        // 在method2中獲取數(shù)據(jù)并打印
        String data = threadLocalData.get();
        System.out.println("方法2拿到的數(shù)據(jù)是:" + data);
    }
}

聊完使用場(chǎng)景和方法,接下來(lái)問(wèn)面試官幾個(gè)問(wèn)題。

問(wèn)題1:請(qǐng)畫(huà)出ThreadLocal和Thread的關(guān)系圖

ThreadLocal和Thread的關(guān)系圖如下。

這里要牢記3點(diǎn):

(1) 數(shù)據(jù)實(shí)際上是存在ThreadLocalMap中的,ThreadLocalMap歸Thread所持有。見(jiàn)源代碼。

(2) ThreadLocalMap內(nèi)部使用的是K-V結(jié)構(gòu),Key是我們定義的ThreadLocal對(duì)象。見(jiàn)源代碼。

(3) ThreadLocalMap對(duì)ThreadLocal是弱引用關(guān)系。見(jiàn)源代碼。

問(wèn)題2:為什么ThreadLocalMap里的Key是弱引用

那為什么ThreadLocalMap里的Key是使用ThreadLocal呢?為什么又是弱引用呢?

這就不得不說(shuō)JDK的設(shè)計(jì)者的思想非常精妙了,有3點(diǎn)妙處:

  • 一個(gè)線程要是存了多種數(shù)據(jù),總得有個(gè)規(guī)則去找他們,那就根據(jù)定義的ThreadLocal對(duì)象去找吧。
  • 對(duì)于開(kāi)發(fā)者來(lái)說(shuō),他只需要使用ThreadLocal去保存數(shù)據(jù)即可,無(wú)需關(guān)系底層結(jié)構(gòu)。也就是說(shuō)對(duì)外暴露簡(jiǎn)單的使用方式即可,對(duì)于不需要調(diào)用方知道的細(xì)節(jié)全部隱藏。
  • 一般情況下Thread的生命周期會(huì)很長(zhǎng),比如Web容器啟動(dòng)后,就會(huì)啟動(dòng)大量的線程丟到線程池中復(fù)用。所以ThreadLocalMap的生命周期也會(huì)很長(zhǎng)。但是,ThreadLocal對(duì)象存在的周期不一定長(zhǎng),如果,ThreadLocalMap的Key對(duì)ThreadLocal是強(qiáng)引用的話,那么ThreadLocal對(duì)象就會(huì)一直存在于內(nèi)存中得不到釋放,最終會(huì)導(dǎo)致內(nèi)存溢出,所以采用了弱引用。

問(wèn)題3:為什么ThreadLocal使用不當(dāng)會(huì)造成內(nèi)存溢出

從上圖的關(guān)系圖可以看出,Value的生命周期是跟著Thread的生命周期來(lái)的,如果一直不處理的話,也會(huì)出現(xiàn)內(nèi)存溢出的情況。

為了避免內(nèi)存溢出的情況,我們?cè)谑褂猛闠hreadLocal后,要即使調(diào)用remove方法,以便JVM回收Value。

總結(jié)

ThreadLocal 是并發(fā)編程中的強(qiáng)大工具,能夠?yàn)槊總€(gè)線程提供獨(dú)立的變量副本,避免線程安全問(wèn)題。并且這個(gè)ThreadLocal存入的值能夠貫穿整個(gè)流程。使用時(shí)要注意上文的幾點(diǎn),防止造成內(nèi)存溢出。

責(zé)任編輯:趙寧寧 來(lái)源: 程序員半支煙
相關(guān)推薦

2024-09-29 16:17:02

2024-11-11 10:40:19

Java變量副本

2022-10-25 10:20:31

線程變量原理

2020-06-16 14:52:41

面試官模型遞歸

2020-10-05 21:46:54

線程

2022-11-04 08:47:52

底層算法數(shù)據(jù)

2024-03-13 07:53:57

弱引用線程工具

2020-04-20 08:35:48

HTTP HTTPS網(wǎng)絡(luò)協(xié)議

2021-08-10 08:34:12

Git ForkBranch

2015-08-13 10:29:12

面試面試官

2025-03-18 12:00:00

閉包JavaScript前端

2019-03-06 14:26:31

Javascript面試前端

2021-05-07 18:12:32

ThreadLocal面試項(xiàng)目

2010-08-23 15:06:52

發(fā)問(wèn)

2025-02-19 00:00:00

RabbitMQTTL插件

2022-05-23 08:43:02

BigIntJavaScript內(nèi)置對(duì)象

2024-11-22 08:31:32

Redis數(shù)據(jù)持久化高可用

2020-04-09 13:38:40

MySQL數(shù)據(jù)庫(kù)臟讀

2021-01-06 08:34:21

Spring核心組件

2010-08-12 16:28:35

面試官
點(diǎn)贊
收藏

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