天天看點

正确了解ThreadLocal

首先,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

<code>private</code> <code>static</code> <code>final</code> <code>threadlocal threadsession = </code><code>new</code> <code>threadlocal();  </code>

<code>  </code> 

<code>public</code> <code>static</code> <code>session getsession() </code><code>throws</code> <code>infrastructureexception {  </code>

<code>    </code><code>session s = (session) threadsession.get();  </code>

<code>    </code><code>try</code> <code>{  </code>

<code>        </code><code>if</code> <code>(s == </code><code>null</code><code>) {  </code>

<code>            </code><code>s = getsessionfactory().opensession();  </code>

<code>            </code><code>threadsession.set(s);  </code>

<code>        </code><code>}  </code>

<code>    </code><code>} </code><code>catch</code> <code>(hibernateexception ex) {  </code>

<code>        </code><code>throw</code> <code>new</code> <code>infrastructureexception(ex);  </code>

<code>    </code><code>}  </code>

<code>    </code><code>return</code> <code>s;  </code>

<code>}</code>

可以看到在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源碼) 

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

<code>public</code> <code>class</code> <code>threadlocal&lt;t&gt; {  </code>

<code>    </code><code>/** </code>

<code>     </code><code>* threadlocals rely on per-thread hash maps attached to each thread </code>

<code>     </code><code>* (thread.threadlocals and inheritablethreadlocals).  the threadlocal </code>

<code>     </code><code>* objects act as keys, searched via threadlocalhashcode.  this is a </code>

<code>     </code><code>* custom hash code (useful only within threadlocalmaps) that eliminates </code>

<code>     </code><code>* collisions in the common case where consecutively constructed </code>

<code>     </code><code>* threadlocals are used by the same threads, while remaining well-behaved </code>

<code>     </code><code>* in less common cases. </code>

<code>     </code><code>*/</code>  

<code>    </code><code>private</code> <code>final</code> <code>int</code> <code>threadlocalhashcode = nexthashcode();  </code>

<code>     </code><code>* the next hash code to be given out. accessed only by like-named method. </code>

<code>    </code><code>private</code> <code>static</code> <code>int</code> <code>nexthashcode = </code><code>0</code><code>;  </code>

<code>     </code><code>* the difference between successively generated hash codes - turns </code>

<code>     </code><code>* implicit sequential thread-local ids into near-optimally spread </code>

<code>     </code><code>* multiplicative hash values for power-of-two-sized tables. </code>

<code>    </code><code>private</code> <code>static</code> <code>final</code> <code>int</code> <code>hash_increment = </code><code>0x61c88647</code><code>;  </code>

<code>     </code><code>* compute the next hash code. the static synchronization used here </code>

<code>     </code><code>* should not be a performance bottleneck. when threadlocals are </code>

<code>     </code><code>* generated in different threads at a fast enough rate to regularly </code>

<code>     </code><code>* contend on this lock, memory contention is by far a more serious </code>

<code>     </code><code>* problem than lock contention. </code>

<code>    </code><code>private</code> <code>static</code> <code>synchronized</code> <code>int</code> <code>nexthashcode() {  </code>

<code>        </code><code>int</code> <code>h = nexthashcode;  </code>

<code>        </code><code>nexthashcode = h + hash_increment;  </code>

<code>        </code><code>return</code> <code>h;  </code>

<code>     </code><code>* creates a thread local variable. </code>

<code>    </code><code>public</code> <code>threadlocal() {  </code>

<code>     </code><code>* returns the value in the current thread's copy of this thread-local </code>

<code>     </code><code>* variable.  creates and initializes the copy if this is the first time </code>

<code>     </code><code>* the thread has called this method. </code>

<code>     </code><code>* </code>

<code>     </code><code>* @return the current thread's value of this thread-local </code>

<code>    </code><code>public</code> <code>t get() {  </code>

<code>        </code><code>thread t = thread.currentthread();  </code>

<code>        </code><code>threadlocalmap map = getmap(t);  </code>

<code>        </code><code>if</code> <code>(map != </code><code>null</code><code>)  </code>

<code>            </code><code>return</code> <code>(t)map.get(</code><code>this</code><code>);  </code>

<code>        </code><code>// maps are constructed lazily.  if the map for this thread  </code>

<code>        </code><code>// doesn't exist, create it, with this threadlocal and its  </code>

<code>        </code><code>// initial value as its only entry.  </code>

<code>        </code><code>t value = initialvalue();  </code>

<code>        </code><code>createmap(t, value);  </code>

<code>        </code><code>return</code> <code>value;  </code>

<code>     </code><code>* sets the current thread's copy of this thread-local variable </code>

<code>     </code><code>* to the specified value.  many applications will have no need for </code>

<code>     </code><code>* this functionality, relying solely on the {@link #initialvalue} </code>

<code>     </code><code>* method to set the values of thread-locals. </code>

<code>     </code><code>* @param value the value to be stored in the current threads' copy of </code>

<code>     </code><code>*        this thread-local. </code>

<code>    </code><code>public</code> <code>void</code> <code>set(t value) {  </code>

<code>            </code><code>map.set(</code><code>this</code><code>, value);  </code>

<code>        </code><code>else</code>  

<code>            </code><code>createmap(t, value);  </code>

<code>     </code><code>* get the map associated with a threadlocal. overridden in </code>

<code>     </code><code>* inheritablethreadlocal. </code>

<code>     </code><code>* @param  t the current thread </code>

<code>     </code><code>* @return the map </code>

<code>    </code><code>threadlocalmap getmap(thread t) {  </code>

<code>        </code><code>return</code> <code>t.threadlocals;  </code>

<code>     </code><code>* create the map associated with a threadlocal. overridden in </code>

<code>     </code><code>* @param t the current thread </code>

<code>     </code><code>* @param firstvalue value for the initial entry of the map </code>

<code>     </code><code>* @param map the map to store. </code>

<code>    </code><code>void</code> <code>createmap(thread t, t firstvalue) {  </code>

<code>        </code><code>t.threadlocals = </code><code>new</code> <code>threadlocalmap(</code><code>this</code><code>, firstvalue);  </code>

<code>    </code><code>.......  </code>

<code>     </code><code>* threadlocalmap is a customized hash map suitable only for </code>

<code>     </code><code>* maintaining thread local values. no operations are exported </code>

<code>     </code><code>* outside of the threadlocal class. the class is package private to </code>

<code>     </code><code>* allow declaration of fields in class thread.  to help deal with </code>

<code>     </code><code>* very large and long-lived usages, the hash table entries use </code>

<code>     </code><code>* weakreferences for keys. however, since reference queues are not </code>

<code>     </code><code>* used, stale entries are guaranteed to be removed only when </code>

<code>     </code><code>* the table starts running out of space. </code>

<code>    </code><code>static</code> <code>class</code> <code>threadlocalmap {  </code>

<code>    </code><code>........  </code>

可以看到threadlocal類中的變量隻有這3個int型: 

正确了解ThreadLocal

<code>private</code> <code>final</code> <code>int</code> <code>threadlocalhashcode = nexthashcode();  </code>

<code>private</code> <code>static</code> <code>int</code> <code>nexthashcode = </code><code>0</code><code>;  </code>

<code>private</code> <code>static</code> <code>final</code> <code>int</code> <code>hash_increment = </code><code>0x61c88647</code><code>;</code>

而作為threadlocal執行個體的變量隻有 threadlocalhashcode 這一個,nexthashcode 和hash_increment 是threadlocal類的靜态變量,實際上hash_increment是一個常量,表示了連續配置設定的兩個threadlocal執行個體的threadlocalhashcode值的增量,而nexthashcode 的表示了即将配置設定的下一個threadlocal執行個體的threadlocalhashcode 的值。 

可以來看一下建立一個threadlocal執行個體即new threadlocal()時做了哪些操作,從上面看到構造函數threadlocal()裡什麼操作都沒有,唯一的操作是這句: 

正确了解ThreadLocal

<code>private</code> <code>final</code> <code>int</code> <code>threadlocalhashcode = nexthashcode();</code>

那麼nexthashcode()做了什麼呢: 

正确了解ThreadLocal

<code>private</code> <code>static</code> <code>synchronized</code> <code>int</code> <code>nexthashcode() {  </code>

<code>    </code><code>int</code> <code>h = nexthashcode;  </code>

<code>    </code><code>nexthashcode = h + hash_increment;  </code>

<code>    </code><code>return</code> <code>h;  </code>

就是将threadlocal類的下一個hashcode值即nexthashcode的值賦給執行個體的threadlocalhashcode,然後nexthashcode的值增加hash_increment這個值。 

是以threadlocal執行個體的變量隻有這個threadlocalhashcode,而且是final的,用來區分不同的threadlocal執行個體,threadlocal類主要是作為工具類來使用,那麼threadlocal.set()進去的對象是放在哪兒的呢? 

看一下上面的set()方法,兩句合并一下成為 

正确了解ThreadLocal

<code>threadlocalmap map = thread.currentthread().threadlocals;</code>

這個threadlocalmap 類是threadlocal中定義的内部類,但是它的執行個體卻用在thread類中: 

正确了解ThreadLocal

<code>public</code> <code>class</code> <code>thread </code><code>implements</code> <code>runnable {  </code>

<code>    </code><code>......  </code>

<code>    </code><code>/* threadlocal values pertaining to this thread. this map is maintained </code>

<code>     </code><code>* by the threadlocal class. */</code>  

<code>    </code><code>threadlocal.threadlocalmap threadlocals = </code><code>null</code><code>;    </code>

再看這句: 

正确了解ThreadLocal

<code>if</code> <code>(map != </code><code>null</code><code>)  </code>

<code>    </code><code>map.set(</code><code>this</code><code>, value);</code>

也就是将該threadlocal執行個體作為key,要保持的對象作為值,設定到目前線程的threadlocalmap 中,get()方法同樣大家看了代碼也就明白了,threadlocalmap 類的代碼太多了,我就不帖了,自己去看源碼吧。 

寫了這麼多,也不知講明白了沒有,有什麼不當的地方還請大家指出來。

特别說明:尊重作者的勞動成果,轉載請注明出處哦~~~http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt107