理解ThreadLocal 2
生活随笔
收集整理的這篇文章主要介紹了
理解ThreadLocal 2
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
為什么80%的碼農都做不了架構師?>>> ??
摘自《Spring 揭密》 王福強著 人民郵電出版社1 ThreadLocal的背景
單單從程序層面來看,我們編寫的代碼實際上是在管理系統中各個對象的相關狀態,如果不能對各個對象的狀態的訪問進行合理的管理,對象的狀態將被破壞,進而導致系統的不正常運行,特別是多線程環境下,多個線程可能對系統之中的單一的或者多個對象狀態進行訪問,如果不能保證在此期間的線程安全,將會把系統帶向崩潰的邊緣。
為了保證整個應用程序的線程安全,我們可以采用多種方式,不過在此之前,我們不妨先看看打架和我們管理線程安全問題有何相似之處。現在你就是系統中的一個對象,假設某一天你出門在外遇到三個歹徒,你毫無退路,只能一搏,你會采用什么樣的策略來保證你的人身安全(線程安全)呢?
策略一,你是一個練家子,而且他人身邊無他人幫助,你只能同事對付三個歹徒,為了不受傷害,你盡量保證每一時刻只對付一名歹徒,以便最終能全身撤退,如果把三名對你的攻擊順序比作三個線程的話,你實際上是在用同步(synchronization)的方式來管理線程對你的訪問。是同同步方式來管理多個線程對對象狀態的訪問以保證應用程序的線程安全是最常用的方式,不過,你也看到了,你需要很辛苦的對待,稍有不慎,就可能受傷,所以這是我們就在想,我要是孫悟空該多好。
“但見那孫猴子揪一撮猴毛一吹,片刻化為多個分身”,“小的們,給我上”。現在我們再也不用苦熬了,讓一個分身對付一個歹徒,讓他們在各自的線程內自己折騰去吧!不用一對三,當然也就不可能破壞到我的完好狀態了!
這就是策略二,通過避免對象的共享,同樣達到了線程安全的目的,你想啊,都在各自的線程內跟各自的分身折騰去了,自然不會需要同步對單一資源的訪問了,ThreadLocal的出現,實際上就是幫助我們以策略二的方式來管理程序的線程安全,只要當前環境允許,能不共享的盡量不共享,反而更容易管理應用程序的線程安全。
綜上來說,同步和ThreadLocal在橫向上可能沒有任何的關系,但從縱向來看,他們實際上都服務于一個目的,那就是幫助我們實現應用程序的線程安全,另外,說ThreadLocal不是用來解決多線程環境下對象共享問題的,也就更好解釋了,ThreadLocal的目的是通過避免對象的共享來保證應用程序的線程安全,共享對象是ThreadLocal盡量避免的,如果要管理的對象非要共享,ThreadLocal自然不會理會這回事了。
2 ThreadLocal的實現
雖然是通過ThreadLocal來設置特定于各個線程的數據資源,單ThreadLocal自身不會保存這些特定的數據資源,因為數據資源特定于線程的,自然是由每個線程自己來管理了
每個Thread類都有一個ThreadLocal.ThreadLocalMap類型的名為threadLocals的實例變量,它就是保持那些通過ThreadLocal設置給當前線程的數據資源的地方,當通過ThreadLocal 的set(data)方法來設置數據的時候,ThreadLocal會首先獲取當前線程的引用,然后通過該引用獲取當前線程持有的threadLocals,最后,以當前的ThreadLocal作為key,將要設置的
數據設置為當前線程,如下所示: Thread thread = Thread.currentThread(); ThreadLocalMap threadLocalMap = thread.threadLocals; ... threadLocalMap.set(this,obj);
而至于余下的get()之類的方法,基本上也是相通的道理,都是首先取得當前線程,然后根據每個方法的語義,對當前線程所持有的threadLocals中的數據進行操作。實際上,ThreadLocal就好像是一個窗口,通過這個窗口,我們可以將特定于線程的數據資源綁定到單簽線程,也可以通過這個窗口獲取綁定的數據資源,當然,更可以接觸之前綁定到當前線程的數據資源,在整個線程的生命周期內,我們都可以通過ThreadLocal這個窗口與當前線程打交道。
(大家可以參考JDK中ThreadLocal和Thread類的源碼!)
為了更好的理解ThreadLocal和Thread的關系,我們不妨設想一下城市的公交系統: 城市中的各條公交線路就好像我們系統中的那一個個線程,在各條公交線路上(比如48),會有相應的公交車輛(一堆48),這些車輛就好像Thread和ThreadLocals。用來運送特定于該條線路的乘客(數據資源)。為了乘客可以乘車或者下車,各條公交線都會設置多個乘車點(Bus Stop),而這些乘車點實際上就是ThreadLocal(比如向陽樓),雖然同一個乘車點可能會有多條公交線路共用,但是同一時間,乘客只會搭乘他要乘坐并且當前經過的公交車,這與ThreadLocal和Thread的關系是類似的,雖然同一個Threadlocal可以為多個線程綁定資源,但只會將數據資源綁定到當前的線程。
是不是有點暈?根據這個公交車的例子,我自己寫了個程序。
BusThreadlocal.java package com.edgar;/** * 模擬公交車站的Threadlocal * @author Edgar * */ public class BusThreadlocal extends ThreadLocal<String> { private String busStopName; //車站的名字public BusThreadlocal(String busStopName) {super();this.busStopName = busStopName; }public String getBusStopName() {return busStopName; }public void setBusStopName(String busStopName) {this.busStopName = busStopName; } }
Bus.java
package com.edgar;import java.util.Random;//公交車線程 public class Bus implements Runnable {private String name; //公交車的名字//這里假設每個公交車都會經過五個站點,結合本例,每個站點都是一個ThreadLocal,一//個站點對應一個Threadlocal對象 private BusThreadlocal stop1; private BusThreadlocal stop2; private BusThreadlocal stop3; private BusThreadlocal stop4; private BusThreadlocal stop5;public Bus(String name, BusThreadlocal stop1, BusThreadlocal stop2,BusThreadlocal stop3, BusThreadlocal stop4, BusThreadlocal stop5) {super();this.name = name;this.stop1 = stop1;this.stop2 = stop2;this.stop3 = stop3;this.stop4 = stop4;this.stop5 = stop5; }@Override public void run() {// TODO Auto-generated method stubRandom rand = new Random();String str = name + ": " + stop1.getBusStopName() + " 到站了。"+ rand.nextInt(10) + "人下車";System.out.println(str);stop1.set(str); //綁定當當前線程str = name + ": " + stop2.getBusStopName() + " 到站了。" + rand.nextInt(10)+ "人下車";System.out.println(str);stop2.set(str);str = name + ": " + stop3.getBusStopName() + " 到站了。" + rand.nextInt(10)+ "人下車";System.out.println(str);stop3.set(str);str = name + ": " + stop4.getBusStopName() + " 到站了。" + rand.nextInt(10)+ "人下車";System.out.println(str);stop4.set(str);str = name + ": " + stop5.getBusStopName() + " 到站了。" + rand.nextInt(10)+ "人下車";System.out.println(str);stop5.set(str);System.out.println("已經到終點站了。");System.out.println(name + " 經過的站點:--------------------");System.out.println(stop1.get()); //取的綁定的值,和set的時候是一樣的。System.out.println(stop2.get());System.out.println(stop3.get());System.out.println(stop4.get());System.out.println(stop5.get()); }}
Test.java
package com.edgar;public class Test {/** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException {// TODO Auto-generated method stubBusThreadlocal l1 = new BusThreadlocal("咸陽北路");BusThreadlocal l2 = new BusThreadlocal("洪湖里");BusThreadlocal l3 = new BusThreadlocal("長虹公園");BusThreadlocal l4 = new BusThreadlocal("天拖大樓");BusThreadlocal l5 = new BusThreadlocal("天津圖書館");BusThreadlocal l6 = new BusThreadlocal("文江東里");BusThreadlocal l7 = new BusThreadlocal("王頂堤");Bus n48_01 = new Bus("第一個48路",l1,l2,l3,l4,l5);//48和911有三個相同的站點Bus n911_01 = new Bus("第一個911路",l6,l2,l3,l4,l7);Bus n911_02 = new Bus("第二個911路",l6,l2,l3,l4,l7);new Thread(n48_01).start();new Thread(n911_01).start();new Thread(n911_02).start(); }}class NO48 implements Runnable{ ThreadLocal<String> threadLocal = new ThreadLocal<String>(); @Override public void run() {// TODO Auto-generated method stubthreadLocal.set(" set value in MyThread.run()+ 當前線程為: "+Thread.currentThread().getName());System.out.println(threadLocal.get());//System.out.println("done"); } }
運行結果:
第二個911路: 文江東里 到站了。0人下車 第二個911路: 洪湖里 到站了。9人下車 第二個911路: 長虹公園 到站了。0人下車 第二個911路: 天拖大樓 到站了。5人下車 第二個911路: 王頂堤 到站了。5人下車 已經到終點站了。 第二個911路 經過的站點:-------------------- 第二個911路: 文江東里 到站了。0人下車 第二個911路: 洪湖里 到站了。9人下車 第二個911路: 長虹公園 到站了。0人下車 第二個911路: 天拖大樓 到站了。5人下車 第二個911路: 王頂堤 到站了。5人下車 第一個48路: 咸陽北路 到站了。1人下車 第一個48路: 洪湖里 到站了。3人下車 第一個48路: 長虹公園 到站了。9人下車 第一個48路: 天拖大樓 到站了。0人下車 第一個48路: 天津圖書館 到站了。7人下車 已經到終點站了。 第一個48路 經過的站點:-------------------- 第一個48路: 咸陽北路 到站了。1人下車 第一個48路: 洪湖里 到站了。3人下車 第一個48路: 長虹公園 到站了。9人下車 第一個48路: 天拖大樓 到站了。0人下車 第一個48路: 天津圖書館 到站了。7人下車 第一個911路: 文江東里 到站了。8人下車 第一個911路: 洪湖里 到站了。4人下車 第一個911路: 長虹公園 到站了。4人下車 第一個911路: 天拖大樓 到站了。7人下車 第一個911路: 王頂堤 到站了。0人下車 已經到終點站了。 第一個911路 經過的站點:-------------------- 第一個911路: 文江東里 到站了。8人下車 第一個911路: 洪湖里 到站了。4人下車 第一個911路: 長虹公園 到站了。4人下車 第一個911路: 天拖大樓 到站了。7人下車 第一個911路: 王頂堤 到站了。0人下車可以看到,一個Threadlocal可以同時使用在多個線程上。并且結果是正確的。
3 Threadlocal的應用場景
基本上,我們可以從2個方面來看待并靈活使用Threadlocal。
1 從橫向上看,我們更注重于Threadlocal橫跨多個線程的能力,這當然是Threadlocal最初的目的,為了以更加方便的方式來管理應用程序的安全,Threadlocal干脆將沒有必要的對象不共享,直接為每個線程分配一份各自特定的數據資源。
2 從縱向上來看,我們著眼于Threadlocal在單一線程內可以發揮的能力,通過Threadlocal設置的特定于各個線程的數據資源,可以隨著所在線程的執行過程“隨波逐流”。
當然,兩個方面并不是獨立的,更多的時候他們是相互依存,緊密耦合的。
例子:我精簡了一下,Connection對象是有狀態并且非線程安全的,為了保證多個線程使用Connection進行數據訪問過程中的安全,我們通過Threadlocal為每個線程分配了一個他們各自持有的Connection,從而避免了對單一Connection資源的爭用。
原文后面還有一些,在這就不介紹了。。。
轉載于:https://my.oschina.net/i33/blog/50515
總結
以上是生活随笔為你收集整理的理解ThreadLocal 2的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [转载]html5教程
- 下一篇: 异步加载 防止图片混乱