多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國(guó)內(nèi)最全I(xiàn)T社區(qū)平臺(tái) 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁(yè) > php開(kāi)源 > php教程 > 深入理解ThreadLocal

深入理解ThreadLocal

來(lái)源:程序員人生   發(fā)布時(shí)間:2015-03-12 09:14:52 閱讀次數(shù):4692次

學(xué)習(xí)1個(gè)東西首先要知道為何要引入它,就是我們能用它來(lái)干甚么。所以我們先來(lái)看看ThreadLocal對(duì)我們到底有甚么用,然后再來(lái)看看它的實(shí)現(xiàn)原理。

ThreadLocal如果單純從名字上來(lái)看像是“本地線(xiàn)程"這么個(gè)意思,只能說(shuō)這個(gè)名字起的確切不太好,很容易讓人產(chǎn)生誤解,ThreadLocalVariable(線(xiàn)程本地變量)應(yīng)當(dāng)是個(gè)更好的名字。我們先看1下官方對(duì)ThreadLocal的描寫(xiě):

該類(lèi)提供了線(xiàn)程局部 (thread-local) 變量。這些變量不同于它們的普通對(duì)應(yīng)物,由于訪(fǎng)問(wèn)某個(gè)變量(通過(guò)其 get 或 set 方法)的每一個(gè)線(xiàn)程都有自己的局部變量,它獨(dú)立于變量的初始化副本。ThreadLocal 實(shí)例通常是類(lèi)中的 private static 字段,它們希望將狀態(tài)與某1個(gè)線(xiàn)程(例如,用戶(hù) ID 或事務(wù) ID)相干聯(lián)。
我們從中摘出要點(diǎn):


1、每一個(gè)線(xiàn)程都有自己的局部變量

    每一個(gè)線(xiàn)程都有1個(gè)獨(dú)立于其他線(xiàn)程的上下文來(lái)保存這個(gè)變量,1個(gè)線(xiàn)程的本地變量對(duì)其他線(xiàn)程是不可見(jiàn)的(有條件,后面解釋?zhuān)?/p>

2、獨(dú)立于變量的初始化副本

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

3、狀態(tài)與某1個(gè)線(xiàn)程相干聯(lián)

    ThreadLocal 不是用于解決同享變量的問(wèn)題的,不是為了調(diào)和線(xiàn)程同步而存在,而是為了方便每一個(gè)線(xiàn)程處理自己的狀態(tài)而引入的1個(gè)機(jī)制,理解這點(diǎn)對(duì)正確使用ThreadLocal相當(dāng)重要。

我們先看1個(gè)簡(jiǎn)單的例子:

public class ThreadLocalTest { //創(chuàng)建1個(gè)Integer型的線(xiàn)程本地變量 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() { //獲得當(dāng)前線(xiàn)程的本地變量,然后累加5次 int num = local.get(); for (int i = 0; i < 5; i++) { num++; } //重新設(shè)置累加后的本地變量 local.set(num); System.out.println(Thread.currentThread().getName() + " : "+ local.get()); } }, "Thread-" + j); } for (Thread thread : threads) { thread.start(); } } }

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

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

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

我們?cè)賮?lái)看1個(gè)例子:

public class ThreadLocalTest { private static Index num = new Index(); //創(chuàng)建1個(gè)Index類(lèi)型的本地變量 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() { //取出當(dāng)前線(xiàn)程的本地變量,并累加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++; } } }

履行后我們發(fā)現(xiàn)結(jié)果以下(每次運(yùn)行還都不1樣):


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

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

-----------------------------------------------低調(diào)的分割線(xiàn)-------------------------------------------

讓我們?cè)賮?lái)回味1下 “ThreadLocal可以給1個(gè)初始值,而每一個(gè)線(xiàn)程都會(huì)取得這個(gè)初始化值的1個(gè)副本” 這句話(huà)。“初始值的副本。。。”,貌似想出發(fā)點(diǎn)甚么。我們?cè)賮?lái)看1下上面代碼中定義ThreadLocal的地方

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

上面代碼中,我們通過(guò)覆蓋initialValue函數(shù)來(lái)給我們的ThreadLocal提供初始值,每一個(gè)線(xiàn)程都會(huì)獲得這個(gè)初始值的1個(gè)副本。而現(xiàn)在我們的初始值是1個(gè)定義好的1個(gè)對(duì)象,num是這個(gè)對(duì)象的援用.換句話(huà)說(shuō)我們的初始值是1個(gè)援用。援用的副本和援用指向的不就是同1個(gè)對(duì)象嗎?

如果我們想給每個(gè)線(xiàn)程都保存1個(gè)Index對(duì)象應(yīng)當(dāng)怎樣辦呢?那就是創(chuàng)建對(duì)象的副本而不是對(duì)象援用的副本:

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

對(duì)象的拷貝圖示:

  

現(xiàn)在我們應(yīng)當(dāng)能明白ThreadLocal本地變量的含義了吧。接下來(lái)我們就來(lái)看看ThreadLocal的源碼,從內(nèi)部來(lái)揭露它的神秘面紗。

ThreadLocal有1個(gè)內(nèi)部類(lèi)ThreadLocalMap,這個(gè)類(lèi)的實(shí)現(xiàn)占了全部ThreadLocal類(lèi)源碼的1多半。這個(gè)ThreadLocalMap的作用非常關(guān)鍵,它就是線(xiàn)程真正保存線(xiàn)程自己本地變量的容器。每個(gè)線(xiàn)程都有自己的單獨(dú)的1個(gè)ThreadLocalMap實(shí)例,其所有的本地變量都會(huì)保存到這1個(gè)map中。現(xiàn)在就讓我們從ThreadLocal的get和set這兩個(gè)最經(jīng)常使用的方法開(kāi)始分析:

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

強(qiáng)調(diào)1下:Thread對(duì)象都有1個(gè)ThreadLocalMap類(lèi)型的屬性threadLocals,這個(gè)屬性是專(zhuān)門(mén)用于保存自己所有的線(xiàn)程本地變量的。這個(gè)屬性在線(xiàn)程對(duì)象初始化的時(shí)候?yàn)閚ull。所以對(duì)1個(gè)線(xiàn)程對(duì)象第1次使用線(xiàn)程本地變量的時(shí)候,需要對(duì)這個(gè)threadLocals屬性進(jìn)行初始化操作。注意要區(qū)分 “線(xiàn)程第1次使用本地線(xiàn)程變量”和“第1次使用某1個(gè)線(xiàn)程本地線(xiàn)程變量”。

getMap方法:

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

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

我們?cè)賮?lái)看1下set方法

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

ThradLocal還有1個(gè)remove方法,讓我們來(lái)分析1下:

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

到這里大家應(yīng)當(dāng)對(duì)ThreadLocal變量比較清晰了,至于ThradLocalMap的實(shí)現(xiàn)細(xì)節(jié)這里就不在說(shuō)了。大家有興趣可以自己去看ThreadLocal的源碼。


生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)
程序員人生
------分隔線(xiàn)----------------------------
分享到:
------分隔線(xiàn)----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 亚洲免费成人网 | 男女激情免费视频 | 欧美日韩国产高清一区二区三区 | 久久国产精品永久免费网站 | 亚洲成年人在线观看 | 欧美亚洲国产精品第一页 | 成在线人免费视频一区二区三区 | 中文字幕亚洲欧美一区 | 中文字幕乱码一二三四区 | 99久久精品毛片免费播放 | 欧美成人性视频播放 | 久久成人小视频 | 国产免费私拍一区二区三区 | 中文字幕亚洲综合久久202 | 免费看w片的网站在线看 | 欧美日韩视频在线播放 | 国产日本韩国 | 日韩中文字幕精品久久 | 日本一区二区三区视频在线观看 | 久久精品国产主播一区二区 | 99精品久久秒播无毒不卡 | 成人欧美在线 | 亚洲视频中文字幕 | 日本大蕉香蕉大视频在线观看 | 国产一级一片免费播放i | 欧美亚洲精品在线 | 亚洲精品在线播放视频 | 国产精品亚洲一区二区三区久久 | 偷柏自拍亚洲欧美综合在线图 | 午夜在线观看视频在线播放版 | 一级性爱视频 | 欧美jizz18性欧美 | 边摸边吃奶边做3p视频 | 国产亚洲3p一区二区三区 | 免费视频网站在线看视频 | 乌克兰性欧美精品高清bd | 久久久国产一区二区三区 | 久久精品一区二区三区不卡 | 日韩欧美国产中文字幕 | japαnese日本丰满护士 | 成人影院久久久久久影院 |