正确理解ThreadLocal
詳見:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt107
首先,ThreadLocal 不是用來解決共享對象的多線程訪問問題的,一般情況下,通過ThreadLocal.set() 到線程中的對象是該線程自己使用的對象,其他線程是不需要訪問的,也訪問不到的。各個線程中訪問的是不同的對象。?
另外,說ThreadLocal使得各線程能夠保持各自獨立的一個對象,并不是通過ThreadLocal.set()來實現的,而是通過每個線程中的new 對象 的操作來創建的對象,每個線程創建一個,不是什么對象的拷貝或副本。通過ThreadLocal.set()將這個新創建的對象的引用保存到各線程的自己的一個map中,每個線程都有這樣一個map,執行ThreadLocal.get()時,各線程從自己的map中取出放進去的對象,因此取出來的是各自自己線程中的對象,ThreadLocal實例是作為map的key來使用的。?
如果ThreadLocal.set()進去的東西本來就是多個線程共享的同一個對象,那么多個線程的ThreadLocal.get()取得的還是這個共享對象本身,還是有并發訪問問題。?
下面來看一個hibernate中典型的ThreadLocal的應用:?
-
?
-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private?static?final?ThreadLocal?threadSession?=?new?ThreadLocal();?????public?static?Session?getSession()?throws?InfrastructureException?{??????Session?s?=?(Session)?threadSession.get();??????try?{??????????if?(s?==?null)?{??????????????s?=?getSessionFactory().openSession();??????????????threadSession.set(s);??????????}??????}?catch?(HibernateException?ex)?{??????????throw?new?InfrastructureException(ex);??????}??????return?s;??}
可以看到在getSession()方法中,首先判斷當前線程中有沒有放進去session,如果還沒有,那么通過sessionFactory().openSession()來創建一個session,再將session set到線程中,實際是放到當前線程的ThreadLocalMap這個map中,這時,對于這個session的唯一引用就是當前線程中的那個ThreadLocalMap(下面會講到),而threadSession作為這個值的key,要取得這個session可以通過threadSession.get()來得到,里面執行的操作實際是先取得當前線程中的ThreadLocalMap,然后將threadSession作為key將對應的值取出。這個session相當于線程的私有變量,而不是public的。?
顯然,其他線程中是取不到這個session的,他們也只能取到自己的ThreadLocalMap中的東西。要是session是多個線程共享使用的,那還不亂套了。?
試想如果不用ThreadLocal怎么來實現呢?可能就要在action中創建session,然后把session一個個傳到service和dao中,這可夠麻煩的。或者可以自己定義一個靜態的map,將當前thread作為key,創建的session作為值,put到map中,應該也行,這也是一般人的想法,但事實上,ThreadLocal的實現剛好相反,它是在每個線程中有一個map,而將ThreadLocal實例作為key,這樣每個map中的項數很少,而且當線程銷毀時相應的東西也一起銷毀了,不知道除了這些還有什么其他的好處。?
總之,ThreadLocal不是用來解決對象共享訪問問題的,而主要是提供了保持對象的方法和避免參數傳遞的方便的對象訪問方式。歸納了兩點:?
1。每個線程中都有一個自己的ThreadLocalMap類對象,可以將線程自己的對象保持到其中,各管各的,線程可以正確的訪問到自己的對象。?
2。將一個共用的ThreadLocal靜態實例作為key,將不同對象的引用保存到不同線程的ThreadLocalMap中,然后在線程執行的各處通過這個靜態ThreadLocal實例的get()方法取得自己線程保存的那個對象,避免了將這個對象作為參數傳遞的麻煩。?
當然如果要把本來線程共享的對象通過ThreadLocal.set()放到線程中也可以,可以實現避免參數傳遞的訪問方式,但是要注意get()到的是那同一個共享對象,并發訪問問題要靠其他手段來解決。但一般來說線程共享的對象通過設置為某類的靜態變量就可以實現方便的訪問了,似乎沒必要放到線程中。?
ThreadLocal的應用場合,我覺得最適合的是按線程多實例(每個線程對應一個實例)的對象的訪問,并且這個對象很多地方都要用到。?
下面來看看ThreadLocal的實現原理(jdk1.5源碼)?
?
-
?
-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 public?class?ThreadLocal<T>?{??????/**??????*?ThreadLocals?rely?on?per-thread?hash?maps?attached?to?each?thread??????*?(Thread.threadLocals?and?inheritableThreadLocals).??The?ThreadLocal??????*?objects?act?as?keys,?searched?via?threadLocalHashCode.??This?is?a??????*?custom?hash?code?(useful?only?within?ThreadLocalMaps)?that?eliminates??????*?collisions?in?the?common?case?where?consecutively?constructed??????*?ThreadLocals?are?used?by?the?same?threads,?while?remaining?well-behaved??????*?in?less?common?cases.??????*/??????private?final?int?threadLocalHashCode?=?nextHashCode();?????????/**??????*?The?next?hash?code?to?be?given?out.?Accessed?only?by?like-named?method.??????*/??????private?static?int?nextHashCode?=?0;?????????/**??????*?The?difference?between?successively?generated?hash?codes?-?turns??????*?implicit?sequential?thread-local?IDs?into?near-optimally?spread??????*?multiplicative?hash?values?for?power-of-two-sized?tables.??????*/??????private?static?final?int?HASH_INCREMENT?=?0x61c88647;?????????/**??????*?Compute?the?next?hash?code.?The?static?synchronization?used?here??????*?should?not?be?a?performance?bottleneck.?When?ThreadLocals?are??????*?generated?in?different?threads?at?a?fast?enough?rate?to?regularly??????*?contend?on?this?lock,?memory?contention?is?by?far?a?more?serious??????*?problem?than?lock?contention.??????*/??????private?static?synchronized?int?nextHashCode()?{??????????int?h?=?nextHashCode;??????????nextHashCode?=?h?+?HASH_INCREMENT;??????????return?h;??????}?????????/**??????*?Creates?a?thread?local?variable.??????*/??????public?ThreadLocal()?{??????}?????????/**??????*?Returns?the?value?in?the?current?thread's?copy?of?this?thread-local??????*?variable.??Creates?and?initializes?the?copy?if?this?is?the?first?time??????*?the?thread?has?called?this?method.??????*??????*?@return?the?current?thread's?value?of?this?thread-local??????*/??????public?T?get()?{??????????Thread?t?=?Thread.currentThread();??????????ThreadLocalMap?map?=?getMap(t);??????????if?(map?!=?null)??????????????return?(T)map.get(this);?????????????//?Maps?are?constructed?lazily.??if?the?map?for?this?thread??????????//?doesn't?exist,?create?it,?with?this?ThreadLocal?and?its??????????//?initial?value?as?its?only?entry.??????????T?value?=?initialValue();??????????createMap(t,?value);??????????return?value;??????}?????????/**??????*?Sets?the?current?thread's?copy?of?this?thread-local?variable??????*?to?the?specified?value.??Many?applications?will?have?no?need?for??????*?this?functionality,?relying?solely?on?the?{@link?#initialValue}??????*?method?to?set?the?values?of?thread-locals.??????*??????*?@param?value?the?value?to?be?stored?in?the?current?threads'?copy?of??????*????????this?thread-local.??????*/??????public?void?set(T?value)?{??????????Thread?t?=?Thread.currentThread();??????????ThreadLocalMap?map?=?getMap(t);??????????if?(map?!=?null)??????????????map.set(this,?value);??????????else??????????????createMap(t,?value);??????}?????????/**??????*?Get?the?map?associated?with?a?ThreadLocal.?Overridden?in??????*?InheritableThreadLocal.??????*??????*?@param??t?the?current?thread??????*?@return?the?map??????*/??????ThreadLocalMap?getMap(Thread?t)?{??????????return?t.threadLocals;??????}?????????/**??????*?Create?the?map?associated?with?a?ThreadLocal.?Overridden?in??????*?InheritableThreadLocal.??????*??????*?@param?t?the?current?thread??????*?@param?firstValue?value?for?the?initial?entry?of?the?map??????*?@param?map?the?map?to?store.??????*/??????void?createMap(Thread?t,?T?firstValue)?{??????????t.threadLocals?=?new?ThreadLocalMap(this,?firstValue);??????}?????????.......?????????/**??????*?ThreadLocalMap?is?a?customized?hash?map?suitable?only?for??????*?maintaining?thread?local?values.?No?operations?are?exported??????*?outside?of?the?ThreadLocal?class.?The?class?is?package?private?to??????*?allow?declaration?of?fields?in?class?Thread.??To?help?deal?with??????*?very?large?and?long-lived?usages,?the?hash?table?entries?use??????*?WeakReferences?for?keys.?However,?since?reference?queues?are?not??????*?used,?stale?entries?are?guaranteed?to?be?removed?only?when??????*?the?table?starts?running?out?of?space.??????*/??????static?class?ThreadLocalMap?{?????????........?????????}?????}
可以看到ThreadLocal類中的變量只有這3個int型:?
Java代碼??
-
?
-
1 2 3 private?final?int?threadLocalHashCode?=?nextHashCode();??private?static?int?nextHashCode?=?0;??private?static?final?int?HASH_INCREMENT?=?0x61c88647;
而作為ThreadLocal實例的變量只有 threadLocalHashCode 這一個,nextHashCode 和HASH_INCREMENT 是ThreadLocal類的靜態變量,實際上HASH_INCREMENT是一個常量,表示了連續分配的兩個ThreadLocal實例的threadLocalHashCode值的增量,而nextHashCode 的表示了即將分配的下一個ThreadLocal實例的threadLocalHashCode 的值。?
可以來看一下創建一個ThreadLocal實例即new ThreadLocal()時做了哪些操作,從上面看到構造函數ThreadLocal()里什么操作都沒有,唯一的操作是這句:?
Java代碼??
-
1 private?final?int?threadLocalHashCode?=?nextHashCode();
那么nextHashCode()做了什么呢:?
Java代碼??
-
?
-
1 2 3 4 5 private?static?synchronized?int?nextHashCode()?{??????int?h?=?nextHashCode;??????nextHashCode?=?h?+?HASH_INCREMENT;??????return?h;??}
就是將ThreadLocal類的下一個hashCode值即nextHashCode的值賦給實例的threadLocalHashCode,然后nextHashCode的值增加HASH_INCREMENT這個值。?
因此ThreadLocal實例的變量只有這個threadLocalHashCode,而且是final的,用來區分不同的ThreadLocal實例,ThreadLocal類主要是作為工具類來使用,那么ThreadLocal.set()進去的對象是放在哪兒的呢??
看一下上面的set()方法,兩句合并一下成為?
Java代碼??
-
1 ThreadLocalMap?map?=?Thread.currentThread().threadLocals;
這個ThreadLocalMap 類是ThreadLocal中定義的內部類,但是它的實例卻用在Thread類中:?
Java代碼??
-
?
-
1 2 3 4 5 6 7 8 public?class?Thread?implements?Runnable?{??????......?????????/*?ThreadLocal?values?pertaining?to?this?thread.?This?map?is?maintained??????*?by?the?ThreadLocal?class.?*/??????ThreadLocal.ThreadLocalMap?threadLocals?=?null;????????......??}
再看這句:?
Java代碼??
-
?
-
1 2 if?(map?!=?null)??????map.set(this,?value);
也就是將該ThreadLocal實例作為key,要保持的對象作為值,設置到當前線程的ThreadLocalMap 中,get()方法同樣大家看了代碼也就明白了,ThreadLocalMap 類的代碼太多了,我就不帖了,自己去看源碼吧。?
寫了這么多,也不知講明白了沒有,有什么不當的地方還請大家指出來。
轉載于:https://www.cnblogs.com/grefr/p/6095064.html
總結
以上是生活随笔為你收集整理的正确理解ThreadLocal的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: IIS 部署 node.js ----
- 下一篇: CST