【Java】深入理解ThreadLocal
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了【Java】深入理解ThreadLocal,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含28322字,纯文字阅读大概需要41分钟。
内容图文
![【Java】深入理解ThreadLocal](/upload/InfoBanner/zyjiaocheng/1317/b4331be8290545ceba92b54afdffc971.jpg)
一、前言
要理解ThreadLocal,首先必须理解线程安全。线程可以看做是一个具有一定独立功能的处理过程,它是比进程更细度的单位。当程序以单线程运行的时候,我们不需要考虑线程安全。然而当一个进程中包含多个线程的时候,就需要考虑线程安全问题,因为此时线程可能会同时操作同一个资源,当两个或者两个以上线程同时操作一个资源的时候,就会造成冲突、不一致等问题,即线程不安全。
解决线程安全问题,本质上就是解决资源共享问题,一般有以下手段:
1)可重入(不依赖环境);2)互斥(同一时间段只允许一个线程使用);3)原子操作;4)Thread-Local
二、Thread-Local
Thread-Local是一个很简单的思想:如果一个资源会引起线程竞争,那就为每一个线程配备一个资源。这就是ThreadLocal需要做的事情。
三、ThreadLocal的用法
在理解ThreadLocal之前,首先看一下它的用法:
1 public class ThreadLocalTest { 2 public static ThreadLocal<Integer> intLocal = new ThreadLocal<Integer>() { 3 @Override 4protected Integer initialValue() { 5// TODO Auto-generated method stub 6return 0; 7 } 8 9 @Override 10public Integer get() { 11// TODO Auto-generated method stub12 set(super.get() + 1); 13returnsuper.get(); 14 } 1516 @Override 17publicvoid set(Integer value) { 18// TODO Auto-generated method stub19super.set(value); 20 } 2122 @Override 23publicvoid remove() { 24// TODO Auto-generated method stub25super.remove(); 26 } 27 }; 2829publicstaticvoid main(String[] args) { 30// TODO Auto-generated method stub31for(int index = 0; index < 3; index ++) 32new MyThread(index).start(); 33 } 34} 3536class MyThread extends Thread{ 37int id; 3839public MyThread(int id){ 40this.id = id; 41 } 4243 @Override 44publicvoid run() { 45// TODO Auto-generated method stub46for(int index = 0; index < 3; index ++){ 47 System.out.println("Thread-" + id + " : " + ThreadLocalTest.intLocal.get()); 48try { 49 Thread.sleep((int)(100 * Math.random())); 50 } catch (InterruptedException e) { 51// TODO Auto-generated catch block52 e.printStackTrace(); 53 } 54 } 55 } 56 }
其打印结果如下:
1 Thread-1 : 1 2 Thread-0 : 1 3 Thread-2 : 1 4 Thread-0 : 2 5 Thread-2 : 2 6 Thread-1 : 2 7 Thread-2 : 3 8 Thread-0 : 3 9 Thread-1 : 3
这是一个很典型的问题,在学习多线程以及同步的时候,几乎所有的书本都会使用类似的一个例子:银行存钱取钱问题。我们知道当多线程并发操作一个int值的加减操作的时候,最后的数值会产生很大的不确定性,得不到最终正确的结果。
而从例子中我们可以看到:每一个线程对int值的操作都是独立的,我们使用的只是同一个静态的intLocal类型!通过使用TreadLocal,我们可以为每一个线程提供独立的资源副本,从而完成对资源的“共享”操作。
ThreadLocal类中可重载的方法只有四个:
1)set():设置值,也就是说,我们选择将某个值设置为ThreadLocal类型的;
2)get():将设置进去的值取出来;
3)remove():我们不想将某个值设置为ThreadLocal了,移除掉;
4)initialValue():如果get的时候还没有设置值,就使用这个方法进行初始化;
使用过程简单明了,一般重载initialValue()提供一个初始值就可以了,其余方法不需要重载。
四、ThreadLocal的实现
看源码是最直接也是最有效的学习方式,不但可以掌握其原理,也可以学习Java源码精巧的实现方式。
ThreadLocal的实现代码略长,我们选取重要的方法作为切入点:
1 public T get() { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4if (map != null) { 5 ThreadLocalMap.Entry e = map.getEntry(this); 6if (e != null) 7return (T)e.value; 8 } 9return setInitialValue(); 10 }
第一个方法是get()方法。这边出现了Thread和Map,联想到Thread的作用,大致应该可以猜到ThreadLocal的实现:维护一个Map,以Thread作为键,以变量作为值,每一次要使用变量的时候,就以当前的Thread作为键去取值,如果没有,就初始化一个值返回,如果有,就直接返回。
事实上,ThreadLocal的实现思路的确大致如此。但是我们要做的事情其实更多:
A.如果要设置更多的值怎么办?也就是说,我们有多种资源需要共享怎么办?
B.为每一个线程共享一个资源,如何回收?
我们言归正传,从源码中获取答案。
get()方法的第3行中出现了一个ThreadLocalMap实例,它是从getMap()方法获取的,其方法如下:
1 ThreadLocalMap getMap(Thread t) { 2 return t.threadLocals; 3 }
t表示当前的线程,从Thread的源码中可以看到,的确是有一个ThreadLocalMap实例,其声明和注解如下:
1 /* ThreadLocal values pertaining to this thread. This map is maintained 2 * by the ThreadLocal class. */ 3 ThreadLocal.ThreadLocalMap threadLocals = null;
这个属性是专门为ThreadLocal类而存在的,而它的实现也存在于ThreadLocal中,是ThreadLocal的一个静态内部类。其类注释如下:
1 /** 2 * ThreadLocalMap is a customized hash map suitable only for 3 * maintaining thread local values. No operations are exported 4 * outside of the ThreadLocal class. The class is package private to 5 * allow declaration of fields in class Thread. To help deal with 6 * very large and long-lived usages, the hash table entries use 7 * WeakReferences for keys. However, since reference queues are not 8 * used, stale entries are guaranteed to be removed only when 9 * the table starts running out of space. 10 */
它是一个定制的HashMap(自然具有HashMap的相关特性,比如自动扩增容量等)。
前面提到A,B两个问题,从源码来看,A问题的解决方案就是,为每一个Thread维护一个HashMap,在这里就是维护一个ThreadLocalMap属性,这个属性的键是ThreadLocal,值就是资源副本,详细描述如下:
每一个Thread都有一个ThreadLocalMap属性,这个属性是类似于HashMap的,它以ThreadLocal为键,以属于该线程的资源副本为值。我们可以这样看待ThreadLocal:ThreadLocal是为一组线程维护资源副本的对象,通过它,可以为每一个线程创建资源副本,也可以正确获得属于某一线程的资源副本。
每一个ThreadLocal只能维护一个共享资源,一旦声明ThreadLocal实例,线程在调用的其get()方法获取资源副本的时候,就可以自动设置绑定到该线程本身。
好,现在转了一小圈回到get方法()。get()方法的第2、3行很明显是获取属于当前线程的ThreadLocalMap,如果这个map不为空,我们就以当前的ThreadLocal为键,去获取相应的Entry,Entry是ThreadLocalMap的静态内部类,其定义如下:
1 static class Entry extends WeakReference<ThreadLocal> { 2/** The value associated with this ThreadLocal. */3 Object value; 45 Entry(ThreadLocal k, Object v) { 6super(k); 7 value = v; 8 } 9 }
它继承与弱引用,所以在get()方法里面如第7行一样调用e.value方法就可以获取实际的资源副本值。但是如果有一个为空,说明属于该线程的资源副本还不存在,则需要去创建资源副本,从代码中可以看到是调用setInitialValue()方法,其定义如下:
1 /** 2 * Variant of set() to establish initialValue. Used instead 3 * of set() in case user has overridden the set() method. 4 * 5 * @return the initial value 6 */ 7 private T setInitialValue() { 8 T value = initialValue(); 9 Thread t = Thread.currentThread(); 10 ThreadLocalMap map = getMap(t); 11if (map != null) 12 map.set(this, value); 13else14 createMap(t, value); 15return value; 16 }
第8行调用initialValue()方法初始化一个值,还记得在一开始的例子中,我们重载这个方法产生一个初始化的值么?
接下来是判断线程的ThreadLocalMap是否为空,不为空就直接这是值(键为this,值为value),为空则创建一个Map,调用方法为createMap(),其定义如下:
1 void createMap(Thread t, T firstValue) { 2 t.threadLocals = new ThreadLocalMap(this, firstValue); 3 }
简单明了,而ThreadLocalMap的这个构造方法的实现如下:
/** * Construct a new map initially containing (firstKey, firstValue). * ThreadLocalMaps are constructed lazily, so we only create * one when we have at least one entry to put in it. */ ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); }
实例化table数组用于存储键值对,然后通过映射将键值对存储进入相应的位置。
至于set方法,看完get()后应该很简单了,自己都可以实现:
1 /** 2 * Sets the current thread‘s copy of this thread-local variable 3 * to the specified value. Most subclasses will have no need to 4 * override this method, relying solely on the { @link #initialValue} 5 * method to set the values of thread-locals. 6 * 7 * @param value the value to be stored in the current thread‘s copy of 8 * this thread-local. 9 */ 10 public void set(T value) { 11 Thread t = Thread.currentThread(); 12 ThreadLocalMap map = getMap(t); 13if (map != null) 14 map.set(this, value); 15else16 createMap(t, value); 17 }
现在还剩一个问题B,会造成内存泄露吗?ThreadLocal的类注释里面有一段话:
/* Each thread holds an implicit reference to its copy of a thread-local * variable as long as the thread is alive and the <tt>ThreadLocal</tt> * instance is accessible; after a thread goes away, all of its copies of * thread-local instances are subject to garbage collection (unless other * references to these copies exist). */
每一个线程对资源副本都有一个隐式引用:只要线程还在运行,只要ThreadLocal还是可以获取的。当一个线程运行结束销毁时,所有的资源副本都是可以被垃圾回收的。这段注释表明,ThreadLocal的使用是不会造成内训泄露的。
但是我们来仔细分析一下,我想画一张Thread、ThreadLocal和ThreadLocalMap的依赖关系图,但是在绘制过程中,我发现:根本就没有依赖!
非常惊讶,我在这里把ThreadLocal完整的源码贴一遍,读者可以自行审视。
1 package java.lang; 2 import java.lang.ref.*; 3import java.util.concurrent.atomic.AtomicInteger; 4 5/** 6 * This class provides thread-local variables. These variables differ from 7 * their normal counterparts in that each thread that accesses one (via its 8 * <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized 9 * copy of the variable. <tt>ThreadLocal</tt> instances are typically private 10 * static fields in classes that wish to associate state with a thread (e.g., 11 * a user ID or Transaction ID). 12 * 13 * <p>For example, the class below generates unique identifiers local to each 14 * thread. 15 * A thread‘s id is assigned the first time it invokes <tt>ThreadId.get()</tt> 16 * and remains unchanged on subsequent calls. 17 * <pre> 18 * import java.util.concurrent.atomic.AtomicInteger; 19 * 20 * public class ThreadId { 21 * // Atomic integer containing the next thread ID to be assigned 22 * private static final AtomicInteger nextId = new AtomicInteger(0); 23 * 24 * // Thread local variable containing each thread‘s ID 25 * private static final ThreadLocal<Integer> threadId = 26 * new ThreadLocal<Integer>() { 27 * @Override protected Integer initialValue() { 28 * return nextId.getAndIncrement(); 29 * } 30 * }; 31 * 32 * // Returns the current thread‘s unique ID, assigning it if necessary 33 * public static int get() { 34 * return threadId.get(); 35 * } 36 * } 37 * </pre> 38 * <p>Each thread holds an implicit reference to its copy of a thread-local 39 * variable as long as the thread is alive and the <tt>ThreadLocal</tt> 40 * instance is accessible; after a thread goes away, all of its copies of 41 * thread-local instances are subject to garbage collection (unless other 42 * references to these copies exist). 43 * 44 * @author Josh Bloch and Doug Lea 45 * @since 1.2 46*/ 47publicclass ThreadLocal<T> { 48/** 49 * ThreadLocals rely on per-thread linear-probe hash maps attached 50 * to each thread (Thread.threadLocals and 51 * inheritableThreadLocals). The ThreadLocal objects act as keys, 52 * searched via threadLocalHashCode. This is a custom hash code 53 * (useful only within ThreadLocalMaps) that eliminates collisions 54 * in the common case where consecutively constructed ThreadLocals 55 * are used by the same threads, while remaining well-behaved in 56 * less common cases. 57*/ 58privatefinalint threadLocalHashCode = nextHashCode(); 59 60/** 61 * The next hash code to be given out. Updated atomically. Starts at 62 * zero. 63*/ 64privatestatic AtomicInteger nextHashCode = 65new AtomicInteger(); 66 67/** 68 * The difference between successively generated hash codes - turns 69 * implicit sequential thread-local IDs into near-optimally spread 70 * multiplicative hash values for power-of-two-sized tables. 71*/ 72privatestaticfinalint HASH_INCREMENT = 0x61c88647; 73 74/** 75 * Returns the next hash code. 76*/ 77privatestaticint nextHashCode() { 78return nextHashCode.getAndAdd(HASH_INCREMENT); 79 } 80 81/** 82 * Returns the current thread‘s "initial value" for this 83 * thread-local variable. This method will be invoked the first 84 * time a thread accesses the variable with the {@link #get} 85 * method, unless the thread previously invoked the {@link #set} 86 * method, in which case the <tt>initialValue</tt> method will not 87 * be invoked for the thread. Normally, this method is invoked at 88 * most once per thread, but it may be invoked again in case of 89 * subsequent invocations of {@link #remove} followed by {@link #get}. 90 * 91 * <p>This implementation simply returns <tt>null</tt>; if the 92 * programmer desires thread-local variables to have an initial 93 * value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be 94 * subclassed, and this method overridden. Typically, an 95 * anonymous inner class will be used. 96 * 97 * @return the initial value for this thread-local 98*/ 99protected T initialValue() { 100returnnull; 101 } 102103/**104 * Creates a thread local variable. 105*/106public ThreadLocal() { 107 } 108109/**110 * Returns the value in the current thread‘s copy of this 111 * thread-local variable. If the variable has no value for the 112 * current thread, it is first initialized to the value returned 113 * by an invocation of the {@link #initialValue} method. 114 * 115 * @return the current thread‘s value of this thread-local 116*/117public T get() { 118 Thread t = Thread.currentThread(); 119 ThreadLocalMap map = getMap(t); 120if (map != null) { 121 ThreadLocalMap.Entry e = map.getEntry(this); 122if (e != null) 123return (T)e.value; 124 } 125return setInitialValue(); 126 } 127128/**129 * Variant of set() to establish initialValue. Used instead 130 * of set() in case user has overridden the set() method. 131 * 132 * @return the initial value 133*/134private T setInitialValue() { 135 T value = initialValue(); 136 Thread t = Thread.currentThread(); 137 ThreadLocalMap map = getMap(t); 138if (map != null) 139 map.set(this, value); 140else141 createMap(t, value); 142return value; 143 } 144145/**146 * Sets the current thread‘s copy of this thread-local variable 147 * to the specified value. Most subclasses will have no need to 148 * override this method, relying solely on the {@link #initialValue} 149 * method to set the values of thread-locals. 150 * 151 * @param value the value to be stored in the current thread‘s copy of 152 * this thread-local. 153*/154publicvoid set(T value) { 155 Thread t = Thread.currentThread(); 156 ThreadLocalMap map = getMap(t); 157if (map != null) 158 map.set(this, value); 159else160 createMap(t, value); 161 } 162163/**164 * Removes the current thread‘s value for this thread-local 165 * variable. If this thread-local variable is subsequently 166 * {@linkplain #get read} by the current thread, its value will be 167 * reinitialized by invoking its {@link #initialValue} method, 168 * unless its value is {@linkplain #set set} by the current thread 169 * in the interim. This may result in multiple invocations of the 170 * <tt>initialValue</tt> method in the current thread. 171 * 172 * @since 1.5 173*/174publicvoid remove() { 175 ThreadLocalMap m = getMap(Thread.currentThread()); 176if (m != null) 177 m.remove(this); 178 } 179180/**181 * Get the map associated with a ThreadLocal. Overridden in 182 * InheritableThreadLocal. 183 * 184 * @param t the current thread 185 * @return the map 186*/187 ThreadLocalMap getMap(Thread t) { 188return t.threadLocals; 189 } 190191/**192 * Create the map associated with a ThreadLocal. Overridden in 193 * InheritableThreadLocal. 194 * 195 * @param t the current thread 196 * @param firstValue value for the initial entry of the map 197 * @param map the map to store. 198*/199void createMap(Thread t, T firstValue) { 200 t.threadLocals = new ThreadLocalMap(this, firstValue); 201 } 202203/**204 * Factory method to create map of inherited thread locals. 205 * Designed to be called only from Thread constructor. 206 * 207 * @param parentMap the map associated with parent thread 208 * @return a map containing the parent‘s inheritable bindings 209*/210static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { 211returnnew ThreadLocalMap(parentMap); 212 } 213214/**215 * Method childValue is visibly defined in subclass 216 * InheritableThreadLocal, but is internally defined here for the 217 * sake of providing createInheritedMap factory method without 218 * needing to subclass the map class in InheritableThreadLocal. 219 * This technique is preferable to the alternative of embedding 220 * instanceof tests in methods. 221*/222 T childValue(T parentValue) { 223thrownew UnsupportedOperationException(); 224 } 225226/**227 * ThreadLocalMap is a customized hash map suitable only for 228 * maintaining thread local values. No operations are exported 229 * outside of the ThreadLocal class. The class is package private to 230 * allow declaration of fields in class Thread. To help deal with 231 * very large and long-lived usages, the hash table entries use 232 * WeakReferences for keys. However, since reference queues are not 233 * used, stale entries are guaranteed to be removed only when 234 * the table starts running out of space. 235*/236staticclass ThreadLocalMap { 237238/**239 * The entries in this hash map extend WeakReference, using 240 * its main ref field as the key (which is always a 241 * ThreadLocal object). Note that null keys (i.e. entry.get() 242 * == null) mean that the key is no longer referenced, so the 243 * entry can be expunged from table. Such entries are referred to 244 * as "stale entries" in the code that follows. 245*/246staticclass Entry extends WeakReference<ThreadLocal> { 247/** The value associated with this ThreadLocal. */248 Object value; 249250 Entry(ThreadLocal k, Object v) { 251super(k); 252 value = v; 253 } 254 } 255256/**257 * The initial capacity -- MUST be a power of two. 258*/259privatestaticfinalint INITIAL_CAPACITY = 16; 260261/**262 * The table, resized as necessary. 263 * table.length MUST always be a power of two. 264*/265private Entry[] table; 266267/**268 * The number of entries in the table. 269*/270privateint size = 0; 271272/**273 * The next size value at which to resize. 274*/275privateint threshold; // Default to 0276277/**278 * Set the resize threshold to maintain at worst a 2/3 load factor. 279*/280privatevoid setThreshold(int len) { 281 threshold = len * 2 / 3; 282 } 283284/**285 * Increment i modulo len. 286*/287privatestaticint nextIndex(int i, int len) { 288return ((i + 1 < len) ? i + 1 : 0); 289 } 290291/**292 * Decrement i modulo len. 293*/294privatestaticint prevIndex(int i, int len) { 295return ((i - 1 >= 0) ? i - 1 : len - 1); 296 } 297298/**299 * Construct a new map initially containing (firstKey, firstValue). 300 * ThreadLocalMaps are constructed lazily, so we only create 301 * one when we have at least one entry to put in it. 302*/303 ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { 304 table = new Entry[INITIAL_CAPACITY]; 305int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); 306 table[i] = new Entry(firstKey, firstValue); 307 size = 1; 308 setThreshold(INITIAL_CAPACITY); 309 } 310311/**312 * Construct a new map including all Inheritable ThreadLocals 313 * from given parent map. Called only by createInheritedMap. 314 * 315 * @param parentMap the map associated with parent thread. 316*/317private ThreadLocalMap(ThreadLocalMap parentMap) { 318 Entry[] parentTable = parentMap.table; 319int len = parentTable.length; 320 setThreshold(len); 321 table = new Entry[len]; 322323for (int j = 0; j < len; j++) { 324 Entry e = parentTable[j]; 325if (e != null) { 326 ThreadLocal key = e.get(); 327if (key != null) { 328 Object value = key.childValue(e.value); 329 Entry c = new Entry(key, value); 330int h = key.threadLocalHashCode & (len - 1); 331while (table[h] != null) 332 h = nextIndex(h, len); 333 table[h] = c; 334 size++; 335 } 336 } 337 } 338 } 339340/**341 * Get the entry associated with key. This method 342 * itself handles only the fast path: a direct hit of existing 343 * key. It otherwise relays to getEntryAfterMiss. This is 344 * designed to maximize performance for direct hits, in part 345 * by making this method readily inlinable. 346 * 347 * @param key the thread local object 348 * @return the entry associated with key, or null if no such 349*/350private Entry getEntry(ThreadLocal key) { 351int i = key.threadLocalHashCode & (table.length - 1); 352 Entry e = table[i]; 353if (e != null && e.get() == key) 354return e; 355else356return getEntryAfterMiss(key, i, e); 357 } 358359/**360 * Version of getEntry method for use when key is not found in 361 * its direct hash slot. 362 * 363 * @param key the thread local object 364 * @param i the table index for key‘s hash code 365 * @param e the entry at table[i] 366 * @return the entry associated with key, or null if no such 367*/368private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) { 369 Entry[] tab = table; 370int len = tab.length; 371372while (e != null) { 373 ThreadLocal k = e.get(); 374if (k == key) 375return e; 376if (k == null) 377 expungeStaleEntry(i); 378else379 i = nextIndex(i, len); 380 e = tab[i]; 381 } 382returnnull; 383 } 384385/**386 * Set the value associated with key. 387 * 388 * @param key the thread local object 389 * @param value the value to be set 390*/391privatevoid set(ThreadLocal key, Object value) { 392393// We don‘t use a fast path as with get() because it is at 394// least as common to use set() to create new entries as 395// it is to replace existing ones, in which case, a fast 396// path would fail more often than not.397398 Entry[] tab = table; 399int len = tab.length; 400int i = key.threadLocalHashCode & (len-1); 401402for (Entry e = tab[i]; 403 e != null; 404 e = tab[i = nextIndex(i, len)]) { 405 ThreadLocal k = e.get(); 406407if (k == key) { 408 e.value = value; 409return; 410 } 411412if (k == null) { 413 replaceStaleEntry(key, value, i); 414return; 415 } 416 } 417418 tab[i] = new Entry(key, value); 419int sz = ++size; 420if (!cleanSomeSlots(i, sz) && sz >= threshold) 421 rehash(); 422 } 423424/**425 * Remove the entry for key. 426*/427privatevoid remove(ThreadLocal key) { 428 Entry[] tab = table; 429int len = tab.length; 430int i = key.threadLocalHashCode & (len-1); 431for (Entry e = tab[i]; 432 e != null; 433 e = tab[i = nextIndex(i, len)]) { 434if (e.get() == key) { 435 e.clear(); 436 expungeStaleEntry(i); 437return; 438 } 439 } 440 } 441442/**443 * Replace a stale entry encountered during a set operation 444 * with an entry for the specified key. The value passed in 445 * the value parameter is stored in the entry, whether or not 446 * an entry already exists for the specified key. 447 * 448 * As a side effect, this method expunges all stale entries in the 449 * "run" containing the stale entry. (A run is a sequence of entries 450 * between two null slots.) 451 * 452 * @param key the key 453 * @param value the value to be associated with key 454 * @param staleSlot index of the first stale entry encountered while 455 * searching for key. 456*/457privatevoid replaceStaleEntry(ThreadLocal key, Object value, 458int staleSlot) { 459 Entry[] tab = table; 460int len = tab.length; 461 Entry e; 462463// Back up to check for prior stale entry in current run. 464// We clean out whole runs at a time to avoid continual 465// incremental rehashing due to garbage collector freeing 466// up refs in bunches (i.e., whenever the collector runs).467int slotToExpunge = staleSlot; 468for (int i = prevIndex(staleSlot, len); 469 (e = tab[i]) != null; 470 i = prevIndex(i, len)) 471if (e.get() == null) 472 slotToExpunge = i; 473474// Find either the key or trailing null slot of run, whichever 475// occurs first476for (int i = nextIndex(staleSlot, len); 477 (e = tab[i]) != null; 478 i = nextIndex(i, len)) { 479 ThreadLocal k = e.get(); 480481// If we find key, then we need to swap it 482// with the stale entry to maintain hash table order. 483// The newly stale slot, or any other stale slot 484// encountered above it, can then be sent to expungeStaleEntry 485// to remove or rehash all of the other entries in run.486if (k == key) { 487 e.value = value; 488489 tab[i] = tab[staleSlot]; 490 tab[staleSlot] = e; 491492// Start expunge at preceding stale entry if it exists493if (slotToExpunge == staleSlot) 494 slotToExpunge = i; 495 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); 496return; 497 } 498499// If we didn‘t find stale entry on backward scan, the 500// first stale entry seen while scanning for key is the 501// first still present in the run.502if (k == null && slotToExpunge == staleSlot) 503 slotToExpunge = i; 504 } 505506// If key not found, put new entry in stale slot507 tab[staleSlot].value = null; 508 tab[staleSlot] = new Entry(key, value); 509510// If there are any other stale entries in run, expunge them511if (slotToExpunge != staleSlot) 512 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); 513 } 514515/**516 * Expunge a stale entry by rehashing any possibly colliding entries 517 * lying between staleSlot and the next null slot. This also expunges 518 * any other stale entries encountered before the trailing null. See 519 * Knuth, Section 6.4 520 * 521 * @param staleSlot index of slot known to have null key 522 * @return the index of the next null slot after staleSlot 523 * (all between staleSlot and this slot will have been checked 524 * for expunging). 525*/526privateint expungeStaleEntry(int staleSlot) { 527 Entry[] tab = table; 528int len = tab.length; 529530// expunge entry at staleSlot531 tab[staleSlot].value = null; 532 tab[staleSlot] = null; 533 size--; 534535// Rehash until we encounter null536 Entry e; 537int i; 538for (i = nextIndex(staleSlot, len); 539 (e = tab[i]) != null; 540 i = nextIndex(i, len)) { 541 ThreadLocal k = e.get(); 542if (k == null) { 543 e.value = null; 544 tab[i] = null; 545 size--; 546 } else { 547int h = k.threadLocalHashCode & (len - 1); 548if (h != i) { 549 tab[i] = null; 550551// Unlike Knuth 6.4 Algorithm R, we must scan until 552// null because multiple entries could have been stale.553while (tab[h] != null) 554 h = nextIndex(h, len); 555 tab[h] = e; 556 } 557 } 558 } 559return i; 560 } 561562/**563 * Heuristically scan some cells looking for stale entries. 564 * This is invoked when either a new element is added, or 565 * another stale one has been expunged. It performs a 566 * logarithmic number of scans, as a balance between no 567 * scanning (fast but retains garbage) and a number of scans 568 * proportional to number of elements, that would find all 569 * garbage but would cause some insertions to take O(n) time. 570 * 571 * @param i a position known NOT to hold a stale entry. The 572 * scan starts at the element after i. 573 * 574 * @param n scan control: <tt>log2(n)</tt> cells are scanned, 575 * unless a stale entry is found, in which case 576 * <tt>log2(table.length)-1</tt> additional cells are scanned. 577 * When called from insertions, this parameter is the number 578 * of elements, but when from replaceStaleEntry, it is the 579 * table length. (Note: all this could be changed to be either 580 * more or less aggressive by weighting n instead of just 581 * using straight log n. But this version is simple, fast, and 582 * seems to work well.) 583 * 584 * @return true if any stale entries have been removed. 585*/586privateboolean cleanSomeSlots(int i, int n) { 587boolean removed = false; 588 Entry[] tab = table; 589int len = tab.length; 590do { 591 i = nextIndex(i, len); 592 Entry e = tab[i]; 593if (e != null && e.get() == null) { 594 n = len; 595 removed = true; 596 i = expungeStaleEntry(i); 597 } 598 } while ( (n >>>= 1) != 0); 599return removed; 600 } 601602/**603 * Re-pack and/or re-size the table. First scan the entire 604 * table removing stale entries. If this doesn‘t sufficiently 605 * shrink the size of the table, double the table size. 606*/607privatevoid rehash() { 608 expungeStaleEntries(); 609610// Use lower threshold for doubling to avoid hysteresis611if (size >= threshold - threshold / 4) 612 resize(); 613 } 614615/**616 * Double the capacity of the table. 617*/618privatevoid resize() { 619 Entry[] oldTab = table; 620int oldLen = oldTab.length; 621int newLen = oldLen * 2; 622 Entry[] newTab = new Entry[newLen]; 623int count = 0; 624625for (int j = 0; j < oldLen; ++j) { 626 Entry e = oldTab[j]; 627if (e != null) { 628 ThreadLocal k = e.get(); 629if (k == null) { 630 e.value = null; // Help the GC631 } else { 632int h = k.threadLocalHashCode & (newLen - 1); 633while (newTab[h] != null) 634 h = nextIndex(h, newLen); 635 newTab[h] = e; 636 count++; 637 } 638 } 639 } 640641 setThreshold(newLen); 642 size = count; 643 table = newTab; 644 } 645646/**647 * Expunge all stale entries in the table. 648*/649privatevoid expungeStaleEntries() { 650 Entry[] tab = table; 651int len = tab.length; 652for (int j = 0; j < len; j++) { 653 Entry e = tab[j]; 654if (e != null && e.get() == null) 655 expungeStaleEntry(j); 656 } 657 } 658 } 659 }
ThreadLocal对Thread的引用全部通过局部变量完成,而没有一个全局变量。而实际的资源副本则存储在Thread的自身的属性ThreadLocalMap中,这说明,其实ThreadLocal只是关联一个Thread和其资源副本的桥梁,并且实际上Thread和资源副本的生命周期是紧密相连的,的的确确如ThreadLocal所说,在线程被回收的时候,其资源副本也会被回收,虽然ThreadLocal是静态的,但是它既不引用Thread,也不引用ThreadLocalMap。真是精巧的设计!
五、ThreadLocal作用
其实ThreadLocal所实现的功能前面已经描述的很清楚了,但是从网上的讨论来看,大家对ThreadLocal所要达成的目的意见却不是很统一,比如博客《彻底理解ThreadLocal》,具体可以看一下类的注释:
/** * This class provides thread-local variables. These variables differ from * their normal counterparts in that each thread that accesses one (via its * <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized * copy of the variable. <tt>ThreadLocal</tt> instances are typically private * static fields in classes that wish to associate state with a thread (e.g., * a user ID or Transaction ID). */
从这个注解来看,和TLS(可以参见《Thread Local Storage》)大致是一样的,其实不用去深究其具体目的。个人觉得,ThreadLocal的功能就是为每一个线程提供一个资源副本,当你有这样的需求的时候,就可以使用ThreadLocal去解决问题。
原文:http://www.cnblogs.com/lqminn/p/3751206.html
内容总结
以上是互联网集市为您收集整理的【Java】深入理解ThreadLocal全部内容,希望文章能够帮你解决【Java】深入理解ThreadLocal所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。