日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > php教程 > 深入理解ThreadLocal

深入理解ThreadLocal

來源:程序員人生   發布時間:2015-03-12 09:14:52 閱讀次數:4698次

學習1個東西首先要知道為何要引入它,就是我們能用它來干甚么。所以我們先來看看ThreadLocal對我們到底有甚么用,然后再來看看它的實現原理。

ThreadLocal如果單純從名字上來看像是“本地線程"這么個意思,只能說這個名字起的確切不太好,很容易讓人產生誤解,ThreadLocalVariable(線程本地變量)應當是個更好的名字。我們先看1下官方對ThreadLocal的描寫:

該類提供了線程局部 (thread-local) 變量。這些變量不同于它們的普通對應物,由于訪問某個變量(通過其 get 或 set 方法)的每一個線程都有自己的局部變量,它獨立于變量的初始化副本。ThreadLocal 實例通常是類中的 private static 字段,它們希望將狀態與某1個線程(例如,用戶 ID 或事務 ID)相干聯。
我們從中摘出要點:


1、每一個線程都有自己的局部變量

    每一個線程都有1個獨立于其他線程的上下文來保存這個變量,1個線程的本地變量對其他線程是不可見的(有條件,后面解釋)

2、獨立于變量的初始化副本

    ThreadLocal可以給1個初始值,而每一個線程都會取得這個初始化值的1個副本,這樣才能保證不同的線程都有1份拷貝。

3、狀態與某1個線程相干聯

    ThreadLocal 不是用于解決同享變量的問題的,不是為了調和線程同步而存在,而是為了方便每一個線程處理自己的狀態而引入的1個機制,理解這點對正確使用ThreadLocal相當重要。

我們先看1個簡單的例子:

public class ThreadLocalTest { //創建1個Integer型的線程本地變量 public static final ThreadLocal<Integer> local = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return 0; } }; public static void main(String[] args) throws InterruptedException { Thread[] threads = new Thread[5]; for (int j = 0; j < 5; j++) { threads[j] = new Thread(new Runnable() { @Override public void run() { //獲得當前線程的本地變量,然后累加5次 int num = local.get(); for (int i = 0; i < 5; i++) { num++; } //重新設置累加后的本地變量 local.set(num); System.out.println(Thread.currentThread().getName() + " : "+ local.get()); } }, "Thread-" + j); } for (Thread thread : threads) { thread.start(); } } }

運行后結果:

Thread-0 : 5
Thread⑷ : 5
Thread⑵ : 5
Thread⑴ : 5
Thread⑶ : 5

我們看到,每一個線程累加后的結果都是5,各個線程處理自己的本地變量值,線程之間互不影響。

我們再來看1個例子:

public class ThreadLocalTest { private static Index num = new Index(); //創建1個Index類型的本地變量 private static ThreadLocal<Index> local = new ThreadLocal<Index>() { @Override protected Index initialValue() { return num; } }; public static void main(String[] args) throws InterruptedException { Thread[] threads = new Thread[5]; for (int j = 0; j < 5; j++) { threads[j] = new Thread(new Runnable() { @Override public void run() { //取出當前線程的本地變量,并累加1000次 Index index = local.get(); for (int i = 0; i < 1000; i++) { index.increase(); } System.out.println(Thread.currentThread().getName() + " : "+ index.num); } }, "Thread-" + j); } for (Thread thread : threads) { thread.start(); } } static class Index { int num; public void increase() { num++; } } }

履行后我們發現結果以下(每次運行還都不1樣):


Thread-0 : 1390
Thread⑵ : 2390
Thread⑷ : 4390
Thread⑶ : 3491
Thread⑴ : 1390

這次為何線程本地變量又失效了呢?大家可以仔細視察上面代碼自己先找1下緣由。

-----------------------------------------------低調的分割線-------------------------------------------

讓我們再來回味1下 “ThreadLocal可以給1個初始值,而每一個線程都會取得這個初始化值的1個副本” 這句話。“初始值的副本。。。”,貌似想出發點甚么。我們再來看1下上面代碼中定義ThreadLocal的地方

private static Index num = new Index(); private static ThreadLocal<Index> local = new ThreadLocal<Index>() { @Override protected Index initialValue() { return num; // 注意這里,返回的是已定義好的對象num,而不是new Index() } };

上面代碼中,我們通過覆蓋initialValue函數來給我們的ThreadLocal提供初始值,每一個線程都會獲得這個初始值的1個副本。而現在我們的初始值是1個定義好的1個對象,num是這個對象的援用.換句話說我們的初始值是1個援用。援用的副本和援用指向的不就是同1個對象嗎?

如果我們想給每個線程都保存1個Index對象應當怎樣辦呢?那就是創建對象的副本而不是對象援用的副本:

private static ThreadLocal<Index> local = new ThreadLocal<Index>() { @Override protected Index initialValue() { return new Index(); //注意這里 } };

對象的拷貝圖示:

  

現在我們應當能明白ThreadLocal本地變量的含義了吧。接下來我們就來看看ThreadLocal的源碼,從內部來揭露它的神秘面紗。

ThreadLocal有1個內部類ThreadLocalMap,這個類的實現占了全部ThreadLocal類源碼的1多半。這個ThreadLocalMap的作用非常關鍵,它就是線程真正保存線程自己本地變量的容器。每個線程都有自己的單獨的1個ThreadLocalMap實例,其所有的本地變量都會保存到這1個map中。現在就讓我們從ThreadLocal的get和set這兩個最經常使用的方法開始分析:

public T get() { //獲得當前履行線程 Thread t = Thread.currentThread(); //獲得當前線程的ThreadLocalMap實例 ThreadLocalMap map = getMap(t); //如果map不為空,說明該線程已有了1個ThreadLocalMap實例 if (map != null) { //map中保存線程的所有的線程本地變量,我們要去查找當前線程本地變量 ThreadLocalMap.Entry e = map.getEntry(this); //如果當前線程本地變量存在這個map中,則返回其對應的值 if (e != null) return (T)e.value; } //如果map不存在或map中不存在當前線程本地變量,返回初始值 return setInitialValue(); }

強調1下:Thread對象都有1個ThreadLocalMap類型的屬性threadLocals,這個屬性是專門用于保存自己所有的線程本地變量的。這個屬性在線程對象初始化的時候為null。所以對1個線程對象第1次使用線程本地變量的時候,需要對這個threadLocals屬性進行初始化操作。注意要區分 “線程第1次使用本地線程變量”和“第1次使用某1個線程本地線程變量”。

getMap方法:

//直接返回線程對象的threadLocals屬性 ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
setInitialValue方法:(看完后再回顧1下之前的那個例子)

private T setInitialValue() { //獲得初始化值,initialValue 就是我們之前覆蓋的方法 T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); //如果map不為空,將初始化值放入到當前線程的ThreadLocalMap對象中 if (map != null) map.set(this, value); else //當前線程第1次使用本地線程變量,需要對map進行初始化工作 createMap(t, value); //返回初始化值 return value; }

我們再來看1下set方法

public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); //說明線程第1次使用線程本地變量(注意這里的第1次含義) else createMap(t, value); }

ThradLocal還有1個remove方法,讓我們來分析1下:

public void remove() { //獲得當前線程的ThreadLocalMap對象 ThreadLocalMap m = getMap(Thread.currentThread()); //如果map不為空,則刪除該本地變量的值 if (m != null) m.remove(this); }

到這里大家應當對ThreadLocal變量比較清晰了,至于ThradLocalMap的實現細節這里就不在說了。大家有興趣可以自己去看ThreadLocal的源碼。


生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 国产精品99一区二区三区 | 4色网| 成人黄色片免费看 | 日本h在线 | 91久久久久久久久久久久久 | 99久久夜色精品国产亚洲96 | 色综合久 | 亚欧美日韩 | 日韩三级电影 | 九九九久久国产免费 | 高清视频一区二区 | 亚洲成人一区在线观看 | 一区二区av在线 | 免费福利在线视频 | 久久久久福利视频 | 欧美在线一区二区三区四区 | 黄色毛片视频免费 | 国产精品av在线 | 青青草一区 | 国产精品久久久久久久久久久免费看 | 亚洲午码| 99精品视频免费在线观看 | 欧美一区三区 | 午夜精品久久久久久久久久久久久 | 日韩欧美一区二区三区免费观看 | 在线观看黄网 | 精品亚洲一区二区 | 日韩欧美在线免费观看 | 瑟瑟在线观看 | 精品久久久影院 | 国产精品久久一区 | 成人午夜网 | 中文字幕亚洲天堂 | 精品欧美一区二区久久久伦 | 成人在线视频看看 | 亚洲乱码一区二区三区三上悠亚 | 精品国产91 | 精品久久黄色 | 成人在线一区二区 | 国产不卡在线播放 | 网曝91综合精品门事件在线 |