欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java線程本地變量ThreadLocal詳解

 更新時(shí)間:2019年06月06日 09:55:09   作者:mayoi7  
ThreadLocal則為每一個(gè)線程提供了一個(gè)變量副本,從而隔離了多個(gè)線程訪問(wèn)數(shù)據(jù)的沖突,ThreadLocal提供了線程安全的對(duì)象封裝,下面我們就來(lái)詳細(xì)了解一下吧

介紹

ThreadLocal作為JDK1.2以來(lái)的一個(gè)java.lang包下的一個(gè)類,在面試和工程中都非常重要,這個(gè)類的主要目的是提供線程本地的變量,所以也有很多地方把這個(gè)類叫做線程本地變量

從字面理解,這個(gè)類為每個(gè)線程都創(chuàng)建了一個(gè)本地變量,實(shí)際上是ThreadLocal為變量在每個(gè)線程中都創(chuàng)建了一個(gè)副本,使得每個(gè)線程都可以訪問(wèn)自己內(nèi)部的副本變量

通常提到多線程,都會(huì)考慮變量同步的問(wèn)題,但是ThreadLocal并不是為了解決多線程共享變量同步的問(wèn)題,而是為了讓每個(gè)線程的變量不互相影響,相當(dāng)于線程之間操縱的都是變量的副本,自然就不用考慮多線程競(jìng)爭(zhēng)的問(wèn)題,也自然沒(méi)有性能損耗

使用方式

先來(lái)看常用的這幾個(gè)方法

public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }

顯而易見(jiàn),get()方法獲取線程擁有的副本值,set()方法進(jìn)行設(shè)值,remove()方法移除,initialValue()進(jìn)行變量初始化,我們先來(lái)看下面這個(gè)實(shí)例,同時(shí)體會(huì)一下應(yīng)用場(chǎng)景

public class Demo {
public static ThreadLocal<Integer> threadLocal = null;
public static void main(String[] args) {
threadLocal = new ThreadLocal<Integer>() {
/**
* 通過(guò)重寫(xiě)該方法來(lái)初始化ThreadLocal的值
*/
@Override
protected Integer initialValue() {
return 10;
}
};
MyThread t1 = new MyThread(20);
MyThread t2 = new MyThread(30);
t1.start();
// 這里為了描述清晰,省略了try-catch語(yǔ)句塊
t1.join();
t2.start();
}
}

在上述方法中,我們定義并初始化一個(gè)ThreadLocal類為10(通過(guò)重寫(xiě)initialValue()方法實(shí)現(xiàn)),然后開(kāi)啟了兩個(gè)線程,同時(shí)我們這里讓t2線程等待t1線程執(zhí)行完再執(zhí)行

MyThread類詳細(xì)信息如下

class MyThread extends Thread {
private int val = 0;
MyThread(int val) {
this.val = val;
}
@Override
public void run() {
System.out.println(Thread.currentThread() + "-BEFORE-" + Demo.threadLocal.get());
Demo.threadLocal.set(val);
System.out.println(Thread.currentThread() + "-AFTER-" + Demo.threadLocal.get());
}
}

我們通過(guò)調(diào)用ThreadLocal對(duì)象的get()方法來(lái)獲取當(dāng)前的值,然后通過(guò)set()方法設(shè)置一個(gè)新值(每個(gè)線程我們?cè)O(shè)置不同的值),然后再通過(guò)get()方法來(lái)獲取設(shè)置后的值

運(yùn)行結(jié)果如下

重點(diǎn)是圖中標(biāo)注的t2線程變量的初始值,雖然我們?cè)趖1線程中修改了變量的值,但是在t2線程中變量值并沒(méi)有被改變,這樣就實(shí)現(xiàn)了每個(gè)線程獨(dú)有的變量

同時(shí),如果一個(gè)ThreadLocal對(duì)象要在很多地方進(jìn)行復(fù)用時(shí),需要在使用前通過(guò)調(diào)用**remove()**方法來(lái)將本地變量恢復(fù)到默認(rèn)值

也許有人會(huì)問(wèn)了,我們給每個(gè)線程定義自己的私有變量不是也可以實(shí)現(xiàn)同樣的操作嗎,理論上當(dāng)然是可行的,但是ThreadLocal遠(yuǎn)比私有變量的形式方便,不僅可以在線程外部進(jìn)行統(tǒng)一的初始化,而且避免在線程內(nèi)部額外設(shè)置變量

原理

點(diǎn)進(jìn)ThreadLocal的源碼中,發(fā)現(xiàn)并沒(méi)有存儲(chǔ)變量的字段值,那看來(lái)ThreadLocal并不負(fù)責(zé)保存變量,我們只能從方法下手

先看initial()方法,畢竟我們的變量默認(rèn)初始值就是在這個(gè)方法中設(shè)置,如下

protected T initialValue() {
return null;
}

我們?cè)诿看蝿?chuàng)建ThreadLocal都要重寫(xiě)這個(gè)方法,那么這個(gè)方法到底在哪調(diào)用呢,我們點(diǎn)進(jìn)get()方法源碼中,如下

public T get() {
// 獲取當(dāng)前線程
Thread t = Thread.currentThread();
// 獲取當(dāng)前線程的map
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
T result = (T)e.value;
return result;
}
}
// 如果map為空則進(jìn)行創(chuàng)建
return setInitialValue();
}

有點(diǎn)眉頭了,我們發(fā)現(xiàn)這里獲取了一個(gè)ThreadLocalMap對(duì)象,所以會(huì)想到有可能是通過(guò)讓線程與變量作一個(gè)KV表,來(lái)實(shí)現(xiàn)每個(gè)線程擁有自己獨(dú)有的變量

我們點(diǎn)進(jìn)getMap(t)方法中,發(fā)現(xiàn)返回了線程t的一個(gè)threadLocals屬性,這是Thread類的一個(gè)字段:

ThreadLocal.ThreadLocalMap threadLocals = null;

這是一個(gè)由ThreadLocal類維護(hù)的屬性,Thread的任何方法都沒(méi)有對(duì)這個(gè)字段進(jìn)行修改操作,而這個(gè)ThreadLocalMap本身又是ThreadLocal的一個(gè)內(nèi)部類,可以把它理解成一個(gè)Map(雖然這個(gè)類沒(méi)有繼承Map接口)

同時(shí)要注意,在ThreadLocalMap對(duì)象中的Entry對(duì)象(鍵值對(duì)),繼承了一個(gè)ThreadLocaMap的弱引用,如下

static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;

Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}

也就是說(shuō),當(dāng)ThreadLocal被置為空時(shí),Entry中的Key則會(huì)在下一次YGC中被回收

我們還是沒(méi)有看到initialValue()方法,別急,點(diǎn)進(jìn)setInitialValue()方法,也就是如果在get()方法中檢測(cè)到map為空時(shí)調(diào)用的方法,如下

private T setInitialValue() {
// 我們?cè)O(shè)定的初始值
T value = initialValue();
// 當(dāng)前線程
Thread t = Thread.currentThread();
// 再檢查一次是否為空
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}

現(xiàn)在基本操作我們都清楚了,set()方法和initialValue()幾乎完全一致,remove()方法則是普通地移除了一個(gè)KV鍵值對(duì)(K為當(dāng)前線程),這里均不再列出,如果感興趣可以自行查看

注意事項(xiàng)

1.臟數(shù)據(jù)

從上面的分析可以看出,ThreadLocal是和Thread綁定的,每一個(gè)Thread對(duì)應(yīng)一個(gè)value,如果沒(méi)有在使用結(jié)束后調(diào)用remove()方法,就會(huì)在下一次重用時(shí)讀到臟數(shù)據(jù)(針對(duì)同一個(gè)線程而言),尤其是使用線程池的場(chǎng)景(線程池中的線程經(jīng)常會(huì)復(fù)用)

2.內(nèi)存泄露

一般在使用時(shí)都會(huì)將ThreadLocal設(shè)置為靜態(tài)字段,這時(shí)候當(dāng)線程執(zhí)行完成后,KV中的V是不會(huì)自動(dòng)回收的,所以要在使用完后及時(shí)調(diào)用remove()方法清理

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java中的位運(yùn)算符號(hào)解讀(&、|、^、~、<<、>>、>>>)

    Java中的位運(yùn)算符號(hào)解讀(&、|、^、~、<<、>>、>>>)

    這篇文章主要介紹了Java中的位運(yùn)算符號(hào)(&、|、^、~、<<、>>、>>>),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • Java日期相關(guān)API的基本操作總結(jié)

    Java日期相關(guān)API的基本操作總結(jié)

    大概總結(jié)一下日期相關(guān)API操作原因是對(duì)于日期的操作我們開(kāi)發(fā)中還是很常見(jiàn)的,包括在數(shù)據(jù)庫(kù)中保存日期,以及之前String類中對(duì)字符串的一些處理開(kāi)發(fā)中都很常見(jiàn),希望對(duì)大家有所幫助
    2022-11-11
  • 解析Java的Jackson庫(kù)中對(duì)象的序列化與數(shù)據(jù)泛型綁定

    解析Java的Jackson庫(kù)中對(duì)象的序列化與數(shù)據(jù)泛型綁定

    這篇文章主要介紹了解析Java的Jackson庫(kù)中對(duì)象的序列化與數(shù)據(jù)泛型綁定,Jackson通常被用來(lái)實(shí)現(xiàn)Java對(duì)象和JSON數(shù)據(jù)的相互轉(zhuǎn)換功能,需要的朋友可以參考下
    2016-01-01
  • 解決IDEA2020.1.2IDEA打不開(kāi)的問(wèn)題(最新分享)

    解決IDEA2020.1.2IDEA打不開(kāi)的問(wèn)題(最新分享)

    由于idea安裝多了某個(gè)jar,點(diǎn)擊出現(xiàn)讀條后閃退情況,接下來(lái)通過(guò)本文給大家分享解決IDEA2020.1.2IDEA打不開(kāi)的問(wèn)題,非常不錯(cuò),具有一定的參考借鑒價(jià)值,感興趣的朋友跟隨小編一起看看吧
    2020-07-07
  • springboot 使用ThreadLocal的實(shí)例代碼

    springboot 使用ThreadLocal的實(shí)例代碼

    這篇文章主要介紹了springboot 使用ThreadLocal的實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 舉例說(shuō)明Java中代碼塊的執(zhí)行順序

    舉例說(shuō)明Java中代碼塊的執(zhí)行順序

    這篇文章主要介紹了舉例說(shuō)明Java中代碼塊的執(zhí)行順序,包括靜態(tài)屬性和非靜態(tài)屬性以及構(gòu)造函數(shù)等相關(guān)的執(zhí)行先后,需要的朋友可以參考下
    2015-07-07
  • SpringBoot項(xiàng)目設(shè)置斷點(diǎn)debug調(diào)試無(wú)效忽略web.xml問(wèn)題的解決

    SpringBoot項(xiàng)目設(shè)置斷點(diǎn)debug調(diào)試無(wú)效忽略web.xml問(wèn)題的解決

    這篇文章主要介紹了SpringBoot項(xiàng)目設(shè)置斷點(diǎn)debug調(diào)試無(wú)效忽略web.xml問(wèn)題的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • 最新log4j2遠(yuǎn)程代碼執(zhí)行漏洞(附解決方法)

    最新log4j2遠(yuǎn)程代碼執(zhí)行漏洞(附解決方法)

    Apache?Log4j2?遠(yuǎn)程代碼執(zhí)行漏洞攻擊代碼,該漏洞利用無(wú)需特殊配置,經(jīng)多方驗(yàn)證,Apache?Struts2、Apache?Solr、Apache?Druid、Apache?Flink等均受影響,本文就介紹一下解決方法
    2021-12-12
  • 使用MAT進(jìn)行JVM內(nèi)存分析實(shí)例

    使用MAT進(jìn)行JVM內(nèi)存分析實(shí)例

    這篇文章主要介紹了使用MAT進(jìn)行JVM內(nèi)存分析實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • Java中this,static,final,const用法詳解

    Java中this,static,final,const用法詳解

    這篇文章主要介紹了Java中this,static,final,const用法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07

最新評(píng)論