标签:before path other invoke moved dde 显示 until using
可以看出不同线程之间 threadLocal 相互独立,互不干扰。
1 public class ThreadLocalTest { 2 3 public static void main(String[] args) { 4 5 Thread t1 = new Thread(new MyRunnable()); 6 Thread t2 = new Thread(new MyRunnable()); 7 t1.start(); 8 t2.start(); 9 10 } 11 12 public static class MyRunnable implements Runnable { 13 14 private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(); 15 16 @Override 17 public void run() { 18 threadLocal.set((int) (Math.random() * 100D)); 19 System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get()); 20 } 21 } 22 23 }
get()方法
/** * Returns the value in the current thread‘s copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread‘s value of this thread-local */ public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); // 当前线程当做key值,去取value if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
set方法:
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); 13 if (map != null) 14 map.set(this, value); // 放入map,key值当前对象 15 else 16 createMap(t, value); 17 }
getMap和createMap方法显示该map类型 ThreadLocalMap
1 /** 2 * Get the map associated with a ThreadLocal. Overridden in 3 * InheritableThreadLocal. 4 * 5 * @param t the current thread 6 * @return the map 7 */ 8 ThreadLocalMap getMap(Thread t) { 9 return t.threadLocals; 10 } 11 12 /** 13 * Create the map associated with a ThreadLocal. Overridden in 14 * InheritableThreadLocal. 15 * 16 * @param t the current thread 17 * @param firstValue value for the initial entry of the map 18 */ 19 void createMap(Thread t, T firstValue) { 20 t.threadLocals = new ThreadLocalMap(this, firstValue); 21 }
ThreadLocalMap 详细定义如下,不展开描述: 有独立的hashcode计算方法
1 static class ThreadLocalMap { 2 3 /** 4 * The entries in this hash map extend WeakReference, using 5 * its main ref field as the key (which is always a 6 * ThreadLocal object). Note that null keys (i.e. entry.get() 7 * == null) mean that the key is no longer referenced, so the 8 * entry can be expunged from table. Such entries are referred to 9 * as "stale entries" in the code that follows. 10 */ 11 static class Entry extends WeakReference<ThreadLocal<?>> { 12 /** The value associated with this ThreadLocal. */ 13 Object value; 14 15 Entry(ThreadLocal<?> k, Object v) { 16 super(k); 17 value = v; 18 } 19 } 20 21 /** 22 * The initial capacity -- MUST be a power of two. 23 */ 24 private static final int INITIAL_CAPACITY = 16; 25 26 /** 27 * The table, resized as necessary. 28 * table.length MUST always be a power of two. 29 */ 30 private Entry[] table; 31 32 /** 33 * The number of entries in the table. 34 */ 35 private int size = 0; 36 37 /** 38 * The next size value at which to resize. 39 */ 40 private int threshold; // Default to 0 41 42 /** 43 * Set the resize threshold to maintain at worst a 2/3 load factor. 44 */ 45 private void setThreshold(int len) { 46 threshold = len * 2 / 3; 47 } 48 49 /** 50 * Increment i modulo len. 51 */ 52 private static int nextIndex(int i, int len) { 53 return ((i + 1 < len) ? i + 1 : 0); 54 } 55 56 /** 57 * Decrement i modulo len. 58 */ 59 private static int prevIndex(int i, int len) { 60 return ((i - 1 >= 0) ? i - 1 : len - 1); 61 } 62 63 /** 64 * Construct a new map initially containing (firstKey, firstValue). 65 * ThreadLocalMaps are constructed lazily, so we only create 66 * one when we have at least one entry to put in it. 67 */ 68 ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { 69 table = new Entry[INITIAL_CAPACITY]; 70 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); 71 table[i] = new Entry(firstKey, firstValue); 72 size = 1; 73 setThreshold(INITIAL_CAPACITY); 74 } 75 76 /** 77 * Construct a new map including all Inheritable ThreadLocals 78 * from given parent map. Called only by createInheritedMap. 79 * 80 * @param parentMap the map associated with parent thread. 81 */ 82 private ThreadLocalMap(ThreadLocalMap parentMap) { 83 Entry[] parentTable = parentMap.table; 84 int len = parentTable.length; 85 setThreshold(len); 86 table = new Entry[len]; 87 88 for (int j = 0; j < len; j++) { 89 Entry e = parentTable[j]; 90 if (e != null) { 91 @SuppressWarnings("unchecked") 92 ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); 93 if (key != null) { 94 Object value = key.childValue(e.value); 95 Entry c = new Entry(key, value); 96 int h = key.threadLocalHashCode & (len - 1); 97 while (table[h] != null) 98 h = nextIndex(h, len); 99 table[h] = c; 100 size++; 101 } 102 } 103 } 104 } 105 106 /** 107 * Get the entry associated with key. This method 108 * itself handles only the fast path: a direct hit of existing 109 * key. It otherwise relays to getEntryAfterMiss. This is 110 * designed to maximize performance for direct hits, in part 111 * by making this method readily inlinable. 112 * 113 * @param key the thread local object 114 * @return the entry associated with key, or null if no such 115 */ 116 private Entry getEntry(ThreadLocal<?> key) { 117 int i = key.threadLocalHashCode & (table.length - 1); 118 Entry e = table[i]; 119 if (e != null && e.get() == key) 120 return e; 121 else 122 return getEntryAfterMiss(key, i, e); 123 } 124 125 /** 126 * Version of getEntry method for use when key is not found in 127 * its direct hash slot. 128 * 129 * @param key the thread local object 130 * @param i the table index for key‘s hash code 131 * @param e the entry at table[i] 132 * @return the entry associated with key, or null if no such 133 */ 134 private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { 135 Entry[] tab = table; 136 int len = tab.length; 137 138 while (e != null) { 139 ThreadLocal<?> k = e.get(); 140 if (k == key) 141 return e; 142 if (k == null) 143 expungeStaleEntry(i); 144 else 145 i = nextIndex(i, len); 146 e = tab[i]; 147 } 148 return null; 149 } 150 151 /** 152 * Set the value associated with key. 153 * 154 * @param key the thread local object 155 * @param value the value to be set 156 */ 157 private void set(ThreadLocal<?> key, Object value) { 158 159 // We don‘t use a fast path as with get() because it is at 160 // least as common to use set() to create new entries as 161 // it is to replace existing ones, in which case, a fast 162 // path would fail more often than not. 163 164 Entry[] tab = table; 165 int len = tab.length; 166 int i = key.threadLocalHashCode & (len-1); 167 168 for (Entry e = tab[i]; 169 e != null; 170 e = tab[i = nextIndex(i, len)]) { 171 ThreadLocal<?> k = e.get(); 172 173 if (k == key) { 174 e.value = value; 175 return; 176 } 177 178 if (k == null) { 179 replaceStaleEntry(key, value, i); 180 return; 181 } 182 } 183 184 tab[i] = new Entry(key, value); 185 int sz = ++size; 186 if (!cleanSomeSlots(i, sz) && sz >= threshold) 187 rehash(); 188 } 189 190 /** 191 * Remove the entry for key. 192 */ 193 private void remove(ThreadLocal<?> key) { 194 Entry[] tab = table; 195 int len = tab.length; 196 int i = key.threadLocalHashCode & (len-1); 197 for (Entry e = tab[i]; 198 e != null; 199 e = tab[i = nextIndex(i, len)]) { 200 if (e.get() == key) { 201 e.clear(); 202 expungeStaleEntry(i); 203 return; 204 } 205 } 206 } 207 208 /** 209 * Replace a stale entry encountered during a set operation 210 * with an entry for the specified key. The value passed in 211 * the value parameter is stored in the entry, whether or not 212 * an entry already exists for the specified key. 213 * 214 * As a side effect, this method expunges all stale entries in the 215 * "run" containing the stale entry. (A run is a sequence of entries 216 * between two null slots.) 217 * 218 * @param key the key 219 * @param value the value to be associated with key 220 * @param staleSlot index of the first stale entry encountered while 221 * searching for key. 222 */ 223 private void replaceStaleEntry(ThreadLocal<?> key, Object value, 224 int staleSlot) { 225 Entry[] tab = table; 226 int len = tab.length; 227 Entry e; 228 229 // Back up to check for prior stale entry in current run. 230 // We clean out whole runs at a time to avoid continual 231 // incremental rehashing due to garbage collector freeing 232 // up refs in bunches (i.e., whenever the collector runs). 233 int slotToExpunge = staleSlot; 234 for (int i = prevIndex(staleSlot, len); 235 (e = tab[i]) != null; 236 i = prevIndex(i, len)) 237 if (e.get() == null) 238 slotToExpunge = i; 239 240 // Find either the key or trailing null slot of run, whichever 241 // occurs first 242 for (int i = nextIndex(staleSlot, len); 243 (e = tab[i]) != null; 244 i = nextIndex(i, len)) { 245 ThreadLocal<?> k = e.get(); 246 247 // If we find key, then we need to swap it 248 // with the stale entry to maintain hash table order. 249 // The newly stale slot, or any other stale slot 250 // encountered above it, can then be sent to expungeStaleEntry 251 // to remove or rehash all of the other entries in run. 252 if (k == key) { 253 e.value = value; 254 255 tab[i] = tab[staleSlot]; 256 tab[staleSlot] = e; 257 258 // Start expunge at preceding stale entry if it exists 259 if (slotToExpunge == staleSlot) 260 slotToExpunge = i; 261 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); 262 return; 263 } 264 265 // If we didn‘t find stale entry on backward scan, the 266 // first stale entry seen while scanning for key is the 267 // first still present in the run. 268 if (k == null && slotToExpunge == staleSlot) 269 slotToExpunge = i; 270 } 271 272 // If key not found, put new entry in stale slot 273 tab[staleSlot].value = null; 274 tab[staleSlot] = new Entry(key, value); 275 276 // If there are any other stale entries in run, expunge them 277 if (slotToExpunge != staleSlot) 278 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); 279 } 280 281 /** 282 * Expunge a stale entry by rehashing any possibly colliding entries 283 * lying between staleSlot and the next null slot. This also expunges 284 * any other stale entries encountered before the trailing null. See 285 * Knuth, Section 6.4 286 * 287 * @param staleSlot index of slot known to have null key 288 * @return the index of the next null slot after staleSlot 289 * (all between staleSlot and this slot will have been checked 290 * for expunging). 291 */ 292 private int expungeStaleEntry(int staleSlot) { 293 Entry[] tab = table; 294 int len = tab.length; 295 296 // expunge entry at staleSlot 297 tab[staleSlot].value = null; 298 tab[staleSlot] = null; 299 size--; 300 301 // Rehash until we encounter null 302 Entry e; 303 int i; 304 for (i = nextIndex(staleSlot, len); 305 (e = tab[i]) != null; 306 i = nextIndex(i, len)) { 307 ThreadLocal<?> k = e.get(); 308 if (k == null) { 309 e.value = null; 310 tab[i] = null; 311 size--; 312 } else { 313 int h = k.threadLocalHashCode & (len - 1); 314 if (h != i) { 315 tab[i] = null; 316 317 // Unlike Knuth 6.4 Algorithm R, we must scan until 318 // null because multiple entries could have been stale. 319 while (tab[h] != null) 320 h = nextIndex(h, len); 321 tab[h] = e; 322 } 323 } 324 } 325 return i; 326 } 327 328 /** 329 * Heuristically scan some cells looking for stale entries. 330 * This is invoked when either a new element is added, or 331 * another stale one has been expunged. It performs a 332 * logarithmic number of scans, as a balance between no 333 * scanning (fast but retains garbage) and a number of scans 334 * proportional to number of elements, that would find all 335 * garbage but would cause some insertions to take O(n) time. 336 * 337 * @param i a position known NOT to hold a stale entry. The 338 * scan starts at the element after i. 339 * 340 * @param n scan control: {@code log2(n)} cells are scanned, 341 * unless a stale entry is found, in which case 342 * {@code log2(table.length)-1} additional cells are scanned. 343 * When called from insertions, this parameter is the number 344 * of elements, but when from replaceStaleEntry, it is the 345 * table length. (Note: all this could be changed to be either 346 * more or less aggressive by weighting n instead of just 347 * using straight log n. But this version is simple, fast, and 348 * seems to work well.) 349 * 350 * @return true if any stale entries have been removed. 351 */ 352 private boolean cleanSomeSlots(int i, int n) { 353 boolean removed = false; 354 Entry[] tab = table; 355 int len = tab.length; 356 do { 357 i = nextIndex(i, len); 358 Entry e = tab[i]; 359 if (e != null && e.get() == null) { 360 n = len; 361 removed = true; 362 i = expungeStaleEntry(i); 363 } 364 } while ( (n >>>= 1) != 0); 365 return removed; 366 } 367 368 /** 369 * Re-pack and/or re-size the table. First scan the entire 370 * table removing stale entries. If this doesn‘t sufficiently 371 * shrink the size of the table, double the table size. 372 */ 373 private void rehash() { 374 expungeStaleEntries(); 375 376 // Use lower threshold for doubling to avoid hysteresis 377 if (size >= threshold - threshold / 4) 378 resize(); 379 } 380 381 /** 382 * Double the capacity of the table. 383 */ 384 private void resize() { 385 Entry[] oldTab = table; 386 int oldLen = oldTab.length; 387 int newLen = oldLen * 2; 388 Entry[] newTab = new Entry[newLen]; 389 int count = 0; 390 391 for (int j = 0; j < oldLen; ++j) { 392 Entry e = oldTab[j]; 393 if (e != null) { 394 ThreadLocal<?> k = e.get(); 395 if (k == null) { 396 e.value = null; // Help the GC 397 } else { 398 int h = k.threadLocalHashCode & (newLen - 1); 399 while (newTab[h] != null) 400 h = nextIndex(h, newLen); 401 newTab[h] = e; 402 count++; 403 } 404 } 405 } 406 407 setThreshold(newLen); 408 size = count; 409 table = newTab; 410 } 411 412 /** 413 * Expunge all stale entries in the table. 414 */ 415 private void expungeStaleEntries() { 416 Entry[] tab = table; 417 int len = tab.length; 418 for (int j = 0; j < len; j++) { 419 Entry e = tab[j]; 420 if (e != null && e.get() == null) 421 expungeStaleEntry(j); 422 } 423 } 424 }
标签:before path other invoke moved dde 显示 until using
原文地址:https://www.cnblogs.com/clarino/p/11717345.html