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

為什么 ThreadLocal 可以做到線程隔離?

開發(fā) 前端
原來 ThreadLocal 就是把我們要傳遞的對象放到了當(dāng)前線程的 threadLocals 屬性中。也就是說每個(gè)線程在用 ThreadLocal 保存對象時(shí),其實(shí)就是將對象放到了當(dāng)前線程實(shí)例對象的 threadLocals 屬性里面。

?對于 ThreadLocal 我們都不陌生,它的作用如同它的名字——用于存放「線程本地」變量。

先通過一個(gè)小例子感受一下:

private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();

public static void main(String[] args) throws Throwable {

Thread threadOne = new Thread(()->{
threadLocal.set("ThreadOne:" + Thread.currentThread().getName());
log.info("線程 One 本地變量值為:{}", threadLocal.get());
threadLocal.remove();
log.info("線程 One remove 后本地變量值為:{}", threadLocal.get());
});

Thread threadTwo = new Thread(()->{
threadLocal.set("ThreadTwo:" + Thread.currentThread().getName());
log.info("線程 Two 本地變量值為:{}", threadLocal.get());
});

threadOne.start();
threadTwo.start();
}

運(yùn)行結(jié)果:

線程 One 本地變量值為:ThreadOne:Thread-0
線程 One remove 后本地變量值為:null
線程 Two 本地變量值為:ThreadTwo:Thread-1

OK,從效果上看,ThreadLocal 確實(shí)是線程隔離的,那么,它是如何做到線程隔離的呢?下面我們扒一扒源碼,看看它是如何做到的:

public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

set() 方法的邏輯如下:

  • 獲取當(dāng)前線程
  • 根據(jù)當(dāng)前線程獲取一個(gè) ThreadLocalMap 對象
  • 如果 map 不為 null 則保存
  • 如果 map 為 null 則創(chuàng)建一個(gè) map

getMap() 和 createMap() 方法都干了啥呢?我們點(diǎn)進(jìn)去看:

ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}

void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

進(jìn)入到兩個(gè)方法內(nèi)部后發(fā)現(xiàn),不管執(zhí)行哪個(gè)分支,最終是把值保存到了當(dāng)前線程的 threadLocals 屬性中。

查看 Thread 類的源碼,你會(huì)發(fā)現(xiàn)類中定義了一個(gè) threadLocals 屬性,且初始值為 null,其類型為ThreadLocal.ThreadLocalMap。

public class Thread implements Runnable {
// ...
ThreadLocal.ThreadLocalMap threadLocals = null;
// ...
}

到此,我們發(fā)現(xiàn)了,原來 ThreadLocal 就是把我們要傳遞的對象放到了當(dāng)前線程的 threadLocals 屬性中。也就是說每個(gè)線程在用 ThreadLocal 保存對象時(shí),其實(shí)就是將對象放到了當(dāng)前線程實(shí)例對象的 threadLocals 屬性里面。這樣一來線程之間自然就是互相獨(dú)立的啦。

再看看 get() 方法:

public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}

private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}

ThreadLocal 的 get() 方法其實(shí)和 set() 方法邏輯很相似,先從當(dāng)前線程的 threadLocals 屬性中取,如果該屬性為 null,那么就初始化。

當(dāng)線程結(jié)束時(shí),會(huì)調(diào)用當(dāng)前線程實(shí)例的 exit() 方法,將 threadLocals 設(shè)置為 null,以便垃圾回收器將其回收掉。

// THread 類中的方法
private void exit() {
// ...
threadLocals = null;
// ...
}

最后,有一點(diǎn)需要格外注意:用完 ThreadLocal 一定要記得手動(dòng)調(diào)用 remove() 方法,否則可能會(huì)產(chǎn)生臟數(shù)據(jù)甚至產(chǎn)生內(nèi)存泄漏。

為啥呢?上面不是說線程結(jié)束時(shí),會(huì)將 threadLocals 置為 null 嗎?

是的,線程結(jié)束時(shí),確實(shí)會(huì)做清理工作。

但,如果線程一直不結(jié)束呢?如果線程會(huì)被復(fù)用呢?比如使用了線程池。

所以,使用 ThreadLocal 一定要手動(dòng) remove()。?

責(zé)任編輯:武曉燕 來源: 今日頭條
相關(guān)推薦

2021-08-10 09:58:59

ThreadLocal內(nèi)存泄漏

2017-02-09 11:54:49

聯(lián)想企業(yè)網(wǎng)盤

2025-04-01 05:22:00

JavaThread變量

2022-10-18 08:38:16

內(nèi)存泄漏線程

2014-09-12 10:23:16

技術(shù)程序員

2024-04-23 08:17:14

MySQL數(shù)據(jù)庫隔離

2018-04-26 13:41:59

IT

2014-09-11 10:42:41

程序員

2021-03-28 23:37:35

線程專屬變量

2024-09-29 08:57:25

2024-12-18 09:03:05

2016-11-01 08:45:31

2023-12-20 14:35:37

Java虛擬線程

2023-03-21 08:02:36

Redis6.0IO多線程

2024-03-13 07:53:57

弱引用線程工具

2020-09-11 06:39:29

ThreadLocal線程

2024-01-19 08:42:45

Java線程字符串

2020-10-23 14:58:07

戴爾

2024-07-16 08:19:46

MySQL數(shù)據(jù)InnoDB

2024-07-03 11:48:08

接口隔離代碼開發(fā)
點(diǎn)贊
收藏

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