老熟女激烈的高潮_日韩一级黄色录像_亚洲1区2区3区视频_精品少妇一区二区三区在线播放_国产欧美日产久久_午夜福利精品导航凹凸

如何掌握ThreadLocal的相關(guān)知識點(diǎn)

本篇內(nèi)容介紹了“如何掌握ThreadLocal的相關(guān)知識點(diǎn)”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

成都創(chuàng)新互聯(lián)始終堅(jiān)持【策劃先行,效果至上】的經(jīng)營理念,通過多達(dá)十載累計(jì)超上千家客戶的網(wǎng)站建設(shè)總結(jié)了一套系統(tǒng)有效的全網(wǎng)推廣解決方案,現(xiàn)已廣泛運(yùn)用于各行各業(yè)的客戶,其中包括:會(huì)所設(shè)計(jì)等企業(yè),備受客戶贊美。

一、介紹

根據(jù) Java 官方文檔的描述,我們可知 ThreadLocal類用于提供線程內(nèi)部的局部變量,其在多線程環(huán)境下能保證各個(gè)線程內(nèi)部變量的隔離性。

換言之,ThreadLocal提供線程內(nèi)的局部變量,不同線程之間不會(huì)相互干擾,該變量作用范圍貫穿線程的生命周期,減少同一線程內(nèi)多個(gè)方法或組件之間一些公共變量傳遞的復(fù)雜度。

二、使用

2.1 常用方法

返回值方法名描述
Tget()返回此線程局部變量的當(dāng)前線程副本中的值
voidremove()移除此線程局部變量當(dāng)前線程的值
voidset(T value)將此線程局部變量的當(dāng)前線程副本中的值設(shè)置為指定值

2.2 案例演示

需求:用 3 名畫家在一個(gè)畫布上各自繪制一種顏色,并打印出其繪制的顏色。

/**
 * 畫布類
 */
public class Canvas {
	
	private String content;

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}
}

/**
 * 畫家類
 */
public class Painter extends Thread {

	private String name;
	
	private Canvas canvas;
	
	private String color;
	
	public Painter(String name, Canvas canvas, String color) {
		this.name = name;
		this.canvas = canvas;
		this.color = color;
	}

	@Override
	public void run() {
		canvas.setContent(color);
		System.out.println(this.name + "在畫板繪制" + canvas.getContent());
		
	}
}

/**
 * 啟動(dòng)類
 */
public class Demo {

	public static void main(String[] args) {
		
		// 創(chuàng)建畫布
		Canvas canvas = new Canvas();
		
		Painter painter1 = new Painter("小強(qiáng)", canvas, "紅色");
		Painter painter2 = new Painter("旺財(cái)", canvas, "黃色");
		Painter painter3 = new Painter("狗蛋", canvas, "藍(lán)色");
		
		painter1.start();
		painter2.start();
		painter3.start();
		
	}
}

執(zhí)行結(jié)果如下:

小強(qiáng)在畫板繪制藍(lán)色
旺財(cái)在畫板繪制黃色
狗蛋在畫板繪制黃色

顯然,在多線程訪問同一個(gè)資源(畫布)的情況下,輸出結(jié)果出現(xiàn)并發(fā)問題。

現(xiàn)有 2 種解決方案:一種是在 run方法中加入 synchronized同步代碼塊,另一種是使用 ThreadLocal改造 Canvas類型。

由于本篇著重介紹 ThreadLocal, 故下邊我們通過第二種方式解決上述問題。

修改 Canvas類為如下:

public class Canvas {
	
	private ThreadLocal map = new ThreadLocal();

	public String getContent() {
		return map.get();
	}

	public void setContent(String content) {
		map.set(content);
	}
}

啟動(dòng)執(zhí)行類,運(yùn)行結(jié)果如下:

小強(qiáng)在畫板繪制紅色
狗蛋在畫板繪制藍(lán)色
旺財(cái)在畫板繪制黃色

結(jié)果正常輸出。

2.3 ThreadLocal 與 synchronized 區(qū)別

名稱原理側(cè)重點(diǎn)
ThreadLocal空間換時(shí)間,每個(gè)線程都都提供一份變量副本,從而實(shí)現(xiàn)同時(shí)訪問而不相互干擾多線程之間資源相互隔離
synchronized時(shí)間換空間,只提供一個(gè)變量,讓線程排隊(duì)訪問多線程之間共享資源,同步訪問

三、ThreadLocal 內(nèi)部結(jié)構(gòu)

在看源碼之前,我們可以試著猜測 ThreadLocal內(nèi)部結(jié)構(gòu)是怎樣的。

比如,ThreadLocal內(nèi)部定義了一個(gè) Map容器。當(dāng)調(diào)用 ThreadLocal實(shí)例的 set方法時(shí),以當(dāng)前線程名/當(dāng)前線程實(shí)例作為 key, 需要保存的內(nèi)容作為 value 進(jìn)行操作。當(dāng)調(diào)用 get方式時(shí),以當(dāng)前線程名/當(dāng)前線程實(shí)例作為 key 獲取數(shù)據(jù)。

上述方案看似可以正常實(shí)現(xiàn)功能,實(shí)則存在一些問題:

1) 由 ThreadLocal 維護(hù) key-value 容器,當(dāng)線程增多并調(diào)用 ThreadLocal 實(shí)例 的set 方法時(shí),key-value 容器也隨之增大,即內(nèi)存占用也隨之增大。

2) 當(dāng)調(diào)用 ThreadLocal 實(shí)例方法的對象為線程池中的線程時(shí),無法區(qū)分線程是否被循環(huán)使用,即當(dāng)前線程之前已從線程池中被拿出調(diào)用 ThreadLocal 實(shí)例的 set 方法,如果當(dāng)前調(diào)用 get 方法就會(huì)取出之前的數(shù)據(jù)造成數(shù)據(jù)污染等問題。

那么,ThreadLocal內(nèi)部到底是怎么實(shí)現(xiàn)線程間內(nèi)部變量的隔離性的呢?

如何掌握ThreadLocal的相關(guān)知識點(diǎn)

如上圖,由 Thread實(shí)例內(nèi)部維護(hù)名為 ThreadLocalMap的容器,其元素是以 ThreadLocal實(shí)例為 key ,保存對象作為 value 的數(shù)據(jù)結(jié)構(gòu),與我們猜測的實(shí)現(xiàn)方式相反。

對比我們之前設(shè)想的方案,JDK 實(shí)現(xiàn)方案有 2 個(gè)好處:

1) Map 存儲(chǔ)的 Entry 數(shù)量變少

2) 當(dāng)線程銷毀時(shí),ThreadLocalMap 也隨之銷毀,減少內(nèi)存使用

四、源碼分析

4.1 ThreadLocal 源碼

我們針對常用的 setgetremove方法進(jìn)行源碼剖析。

public void set(T value) {
    // 獲取當(dāng)前線程對象
    Thread t = Thread.currentThread();
    // 獲取當(dāng)前線程對象維護(hù)的 ThreadLocalMap 對象
    ThreadLocalMap map = getMap(t);
    if (map != null)
        // 如果 map 存在設(shè)置 entry
        map.set(this, value);
    else
        // 如果 map 不存在,由于 threadLocal 實(shí)例幫忙創(chuàng)建并綁定數(shù)據(jù)
        createMap(t, value);
}

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

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

set 方法執(zhí)行流程:

1) 獲取當(dāng)前線程對象
2) 通過當(dāng)前線程對象獲取 ThreadLocalMap 對象
3) 如果 ThreadLocalMap 對象存在,則將入?yún)⒃O(shè)置進(jìn) ThreadLocalMap 對象中
4) 如果 ThreadLocalMap 對象不存在,則給當(dāng)前線程創(chuàng)建 ThreadLocalMap 對象并設(shè)置入?yún)?/pre>


public T get() {
    // 獲取當(dāng)前線程對象
    Thread t = Thread.currentThread();
    // 獲取當(dāng)前線程對象維護(hù)的 ThreadLocalMap 對象
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 如果 map 不為空,以當(dāng)前的 ThreadLocal 實(shí)例為 key, 獲取數(shù)據(jù)
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // 如果 map 為空,初始化值,通常為 null
    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;
}

protected T initialValue() {
    return null;
}

get 方法執(zhí)行流程:

1) 獲取當(dāng)前線程對象
2) 通過當(dāng)前線程對象獲取 ThreadLocalMap 對象
3) 如果 ThreadLocalMap 對象存在,則以當(dāng)前的 ThreadLocal 實(shí)例為 key, 獲取數(shù)據(jù)
4) 如果 ThreadLocalMap 對象不存在,則通過 initialValue 方法初始化 value 值。


public void remove() {
     ThreadLocalMap m = getMap(Thread.currentThread());
     if (m != null)
         m.remove(this);
 }

remove 方法執(zhí)行流程:

1) 通過當(dāng)前線程對象獲取 ThreadLocalMap 對象
2) 如果 ThreadLocalMap 對象存在,則以當(dāng)前的 ThreadLocal 實(shí)例為 key, 進(jìn)行數(shù)據(jù)刪除

4.2 ThreadLocalMap 源碼

ThreadLocalMapThreadLocal的內(nèi)部類,其沒有實(shí)現(xiàn) Map接口,單獨(dú)實(shí)現(xiàn)了 Map的功能。

成員變量:

/**
 * 初始容量,必須是 2 的整次冪
 */
private static final int INITIAL_CAPACITY = 16;

/**
 * 存放數(shù)據(jù)的 table,數(shù)據(jù)長度也是 2 的整次冪
 */
private Entry[] table;

/**
 * 數(shù)組中 entry 的個(gè)數(shù)
 */
private int size = 0;

/**
 * 進(jìn)行擴(kuò)展的閥值
 */
private int threshold; // Default to 0

Entry 內(nèi)部類:

static class Entry extends WeakReference> {

    Object value;

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

Entry繼承 WeakReference類,也就是 key 是弱引用,其目的是將 ThreadLocal對象的生命周期與線程的生命周期解綁。

五、內(nèi)存泄漏

雖然 ThreadLocal作為弱引用 key 來使用,但是在某些情況下還是會(huì)造成內(nèi)存泄漏問題。 在分析內(nèi)存泄漏之前,我們先補(bǔ)充幾個(gè)概念:

內(nèi)存溢出:沒有足夠的內(nèi)存供申請者使用

內(nèi)存泄漏:程序中已動(dòng)態(tài)分配的堆內(nèi)存由于某種原因未釋放或無法釋放,造成系統(tǒng)內(nèi)存浪費(fèi),導(dǎo)致程序運(yùn)行速度減慢甚至系統(tǒng)崩潰等嚴(yán)重后果,該問題最終會(huì)導(dǎo)致內(nèi)存溢出

強(qiáng)引用:常見的對象引用,只要還有強(qiáng)引用指向一個(gè)對象,表明對象還“活著”,垃圾回收器就不會(huì)回收該對象

弱引用:垃圾回收期一旦發(fā)現(xiàn)只具有弱引用指向的對象,不管當(dāng)前內(nèi)存空間是否足夠,都會(huì)回收該對象

了解了基本概念,接下來我們分析使用 ThreadLocal出現(xiàn)內(nèi)存泄漏的情況:

如何掌握ThreadLocal的相關(guān)知識點(diǎn)

上圖為一個(gè)線程使用 ThreacLocal時(shí)的內(nèi)存結(jié)構(gòu)圖,實(shí)線箭頭表示強(qiáng)引用,虛線箭頭表示弱引用。

當(dāng) ThreadLocal 使用結(jié)束,棧內(nèi)存的 ThreadLocal 引用被回收,即引用 1 不再指向 ThreadLocal 對象。

由于引用 2 是弱引用,沒有任何強(qiáng)引用指向 ThreadLocal 對象,因此 ThreadLocal 對象會(huì)被 GC 回收,此時(shí) Entry 的 key = null

如果我們沒有會(huì)手動(dòng)刪除 Entry 對象,且當(dāng)前線程一直在運(yùn)行中,會(huì)存在一個(gè)強(qiáng)引用鏈 Thread 引用-> Thread 對象-> ThreadLocal 對象-> Entry 對象 -> Value,由于 value 不會(huì)被回收,而 key 又為 null, value 這塊內(nèi)存就永遠(yuǎn)無法被訪問,這就造成了內(nèi)存泄漏,

既然使用弱引用作為 ThreadLocalMap的 key 會(huì)造成內(nèi)存泄漏,那為什么還要使用它呢?

其實(shí),在 ThreadLocalMapsetgetEntry方法中,會(huì)對 key 為 null 進(jìn)行判斷,如果為 null, 那么會(huì)將 value 也設(shè)置為 null。

換言之,在使用 ThreadLocal的線程依然運(yùn)行的情況下,我們忘記調(diào)用 remove方法,弱引用比強(qiáng)引用多一層保障。弱引用指向的 ThreadLocal對象被回收,對應(yīng)的 value 在 TheadLocalMap調(diào)用 setgetEntryremove任一方法時(shí)被設(shè)置為 null, 避免內(nèi)存泄漏。

六、總結(jié)

適用于多線程并發(fā)場景

使用 ThreadLocal 在同一線程,不同組件中可傳遞公共變量

每個(gè)線程的變量都是相互獨(dú)立,互不影響

“如何掌握ThreadLocal的相關(guān)知識點(diǎn)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!


本文題目:如何掌握ThreadLocal的相關(guān)知識點(diǎn)
標(biāo)題鏈接:http://www.xueling.net.cn/article/ghcosd.html

其他資訊

在線咨詢
服務(wù)熱線
服務(wù)熱線:028-86922220
TOP
主站蜘蛛池模板: 在线观看免费91 | 国产专区中文字幕 | а天堂中文最新版在线 | 免费在线观看av的网站 | 麻豆精品久久久久久中文字幕无码 | 亚洲日韩中文在线精品第一 | 国产精品久久久久久久久久嫩草 | 久久精品免费 | 国产一区二区三区免费在线观看 | 2020国产精品香蕉在线观看 | 国产精品28P | 午夜久久久久久久久久 | 精品久久午夜 | 成人精品一区二区三区校园激情 | 春药刺激国产老富婆露脸 | 精品国产一区二区三 | 精品女同一区二区三区在线 | 日本理论在线播放 | 69av在线播放 | XXX性XXX国语对白 | 东京热无码AV在线 | 亚洲成aⅴ人片在线观 | 无码无遮挡又大又爽又黄的视频 | 末成年女av片一区二区 | 品色堂永远免费论坛 | 亚洲精品入口a级 | 亚洲国产成人无码AV在线播放 | 天天透天天狠天天爱综合97 | 亚洲国产成人va在线观看天堂 | 伊人精品 | 99在线视频免费观看 | 久久不卡| 青青草视频在线免费播放 | 国产精品va无码免费麻豆 | 亚洲第一色 | 狠狠操在线视频 | 欧美亚洲精品一区二区在线观看 | 亚洲人a成www在线影院 | 亚洲午夜精品无码专区在线观看 | 日韩欧美中文字幕一区二区 | 亚洲狼人天堂网 |