博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java基础--ThreadLocal
阅读量:5273 次
发布时间:2019-06-14

本文共 23826 字,大约阅读时间需要 79 分钟。

Java中的ThreadLocal 可以看做以线程标识为key的Map,在多线程开发中使用非常方便。

示例

1 class ThreadEnv { 2      3     // 用匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值 4     private static ThreadLocal
threadId = new ThreadLocal
() { 5 @Override 6 protected Integer initialValue() { 7 return 10; 8 } 9 };10 11 public int get() {12 // 第一次get到的是初始值13 int a = threadId.get();14 a++;15 threadId.set(a);16 return a;17 }18 }19 20 public class ThreadLocalTest {21 22 public static void main(String[] args) {23 ThreadEnv sn = new ThreadEnv();24 new TestClient(sn);25 new TestClient(sn);26 new TestClient(sn);27 }28 29 private static class TestClient extends Thread {30 private ThreadEnv sn;31 32 public TestClient(ThreadEnv sn) {33 this.sn = sn;34 this.start();35 }36 37 public void run() {38 for (int i = 0; i < 3; i++) {39 System.out.println(Thread.currentThread()+ " >>> " + sn.get());40 }41 }42 }43 44 }

运行结果

Thread[Thread-2,5,main] >>> 11Thread[Thread-1,5,main] >>> 11Thread[Thread-0,5,main] >>> 11Thread[Thread-1,5,main] >>> 12Thread[Thread-1,5,main] >>> 13Thread[Thread-2,5,main] >>> 12Thread[Thread-0,5,main] >>> 12Thread[Thread-0,5,main] >>> 13Thread[Thread-2,5,main] >>> 13

源码解析

1     public ThreadLocal() {  2     }  3   4     /**  5      * Returns the value in the current thread's copy of this  6      * thread-local variable.  If the variable has no value for the  7      * current thread, it is first initialized to the value returned  8      * by an invocation of the {
@link #initialValue} method. 9 * 10 * @return the current thread's value of this thread-local 11 */ 12 public T get() { 13 Thread t = Thread.currentThread(); 14 ThreadLocalMap map = getMap(t); 15 if (map != null) { 16 ThreadLocalMap.Entry e = map.getEntry(this); 17 if (e != null) { 18 @SuppressWarnings("unchecked") 19 T result = (T)e.value; 20 return result; 21 } 22 } 23 return setInitialValue(); 24 } 25 26 /** 27 * Variant of set() to establish initialValue. Used instead 28 * of set() in case user has overridden the set() method. 29 * 30 * @return the initial value 31 */ 32 private T setInitialValue() { 33 T value = initialValue(); 34 Thread t = Thread.currentThread(); 35 ThreadLocalMap map = getMap(t); 36 if (map != null) 37 map.set(this, value); 38 else 39 createMap(t, value); 40 return value; 41 } 42 43 /** 44 * Sets the current thread's copy of this thread-local variable 45 * to the specified value. Most subclasses will have no need to 46 * override this method, relying solely on the {
@link #initialValue} 47 * method to set the values of thread-locals. 48 * 49 * @param value the value to be stored in the current thread's copy of 50 * this thread-local. 51 */ 52 public void set(T value) { 53 Thread t = Thread.currentThread(); 54 ThreadLocalMap map = getMap(t); 55 if (map != null) 56 map.set(this, value); 57 else 58 createMap(t, value); 59 } 60 61 /** 62 * Removes the current thread's value for this thread-local 63 * variable. If this thread-local variable is subsequently 64 * {
@linkplain #get read} by the current thread, its value will be 65 * reinitialized by invoking its {
@link #initialValue} method, 66 * unless its value is {
@linkplain #set set} by the current thread 67 * in the interim. This may result in multiple invocations of the 68 * {
@code initialValue} method in the current thread. 69 * 70 * @since 1.5 71 */ 72 public void remove() { 73 ThreadLocalMap m = getMap(Thread.currentThread()); 74 if (m != null) 75 m.remove(this); 76 } 77 78 /** 79 * Get the map associated with a ThreadLocal. Overridden in 80 * InheritableThreadLocal. 81 * 82 * @param t the current thread 83 * @return the map 84 */ 85 ThreadLocalMap getMap(Thread t) { 86 return t.threadLocals; 87 } 88 89 /** 90 * Create the map associated with a ThreadLocal. Overridden in 91 * InheritableThreadLocal. 92 * 93 * @param t the current thread 94 * @param firstValue value for the initial entry of the map 95 */ 96 void createMap(Thread t, T firstValue) { 97 t.threadLocals = new ThreadLocalMap(this, firstValue); 98 } 99 100 /**101 * Factory method to create map of inherited thread locals.102 * Designed to be called only from Thread constructor.103 *104 * @param parentMap the map associated with parent thread105 * @return a map containing the parent's inheritable bindings106 */107 static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {108 return new ThreadLocalMap(parentMap);109 }110 111 /**112 * Method childValue is visibly defined in subclass113 * InheritableThreadLocal, but is internally defined here for the114 * sake of providing createInheritedMap factory method without115 * needing to subclass the map class in InheritableThreadLocal.116 * This technique is preferable to the alternative of embedding117 * instanceof tests in methods.118 */119 T childValue(T parentValue) {120 throw new UnsupportedOperationException();121 }122 123 /**124 * An extension of ThreadLocal that obtains its initial value from125 * the specified {
@code Supplier}.126 */127 static final class SuppliedThreadLocal
extends ThreadLocal
{128 129 private final Supplier
supplier;130 131 SuppliedThreadLocal(Supplier
supplier) {132 this.supplier = Objects.requireNonNull(supplier);133 }134 135 @Override136 protected T initialValue() {137 return supplier.get();138 }139 }140 141 /**142 * ThreadLocalMap is a customized hash map suitable only for143 * maintaining thread local values. No operations are exported144 * outside of the ThreadLocal class. The class is package private to145 * allow declaration of fields in class Thread. To help deal with146 * very large and long-lived usages, the hash table entries use147 * WeakReferences for keys. However, since reference queues are not148 * used, stale entries are guaranteed to be removed only when149 * the table starts running out of space.150 */151 static class ThreadLocalMap {152 153 /**154 * The entries in this hash map extend WeakReference, using155 * its main ref field as the key (which is always a156 * ThreadLocal object). Note that null keys (i.e. entry.get()157 * == null) mean that the key is no longer referenced, so the158 * entry can be expunged from table. Such entries are referred to159 * as "stale entries" in the code that follows.160 */161 static class Entry extends WeakReference
> {162 /** The value associated with this ThreadLocal. */163 Object value;164 165 Entry(ThreadLocal
k, Object v) {166 super(k);167 value = v;168 }169 }170 171 /**172 * The initial capacity -- MUST be a power of two.173 */174 private static final int INITIAL_CAPACITY = 16;175 176 /**177 * The table, resized as necessary.178 * table.length MUST always be a power of two.179 */180 private Entry[] table;181 182 /**183 * The number of entries in the table.184 */185 private int size = 0;186 187 /**188 * The next size value at which to resize.189 */190 private int threshold; // Default to 0191 192 /**193 * Set the resize threshold to maintain at worst a 2/3 load factor.194 */195 private void setThreshold(int len) {196 threshold = len * 2 / 3;197 }198 199 /**200 * Increment i modulo len.201 */202 private static int nextIndex(int i, int len) {203 return ((i + 1 < len) ? i + 1 : 0);204 }205 206 /**207 * Decrement i modulo len.208 */209 private static int prevIndex(int i, int len) {210 return ((i - 1 >= 0) ? i - 1 : len - 1);211 }212 213 /**214 * Construct a new map initially containing (firstKey, firstValue).215 * ThreadLocalMaps are constructed lazily, so we only create216 * one when we have at least one entry to put in it.217 */218 ThreadLocalMap(ThreadLocal
firstKey, Object firstValue) {219 table = new Entry[INITIAL_CAPACITY];220 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);221 table[i] = new Entry(firstKey, firstValue);222 size = 1;223 setThreshold(INITIAL_CAPACITY);224 }225 226 /**227 * Construct a new map including all Inheritable ThreadLocals228 * from given parent map. Called only by createInheritedMap.229 *230 * @param parentMap the map associated with parent thread.231 */232 private ThreadLocalMap(ThreadLocalMap parentMap) {233 Entry[] parentTable = parentMap.table;234 int len = parentTable.length;235 setThreshold(len);236 table = new Entry[len];237 238 for (int j = 0; j < len; j++) {239 Entry e = parentTable[j];240 if (e != null) {241 @SuppressWarnings("unchecked")242 ThreadLocal
key = (ThreadLocal) e.get();243 if (key != null) {244 Object value = key.childValue(e.value);245 Entry c = new Entry(key, value);246 int h = key.threadLocalHashCode & (len - 1);247 while (table[h] != null)248 h = nextIndex(h, len);249 table[h] = c;250 size++;251 }252 }253 }254 }255 256 /**257 * Get the entry associated with key. This method258 * itself handles only the fast path: a direct hit of existing259 * key. It otherwise relays to getEntryAfterMiss. This is260 * designed to maximize performance for direct hits, in part261 * by making this method readily inlinable.262 *263 * @param key the thread local object264 * @return the entry associated with key, or null if no such265 */266 private Entry getEntry(ThreadLocal
key) {267 int i = key.threadLocalHashCode & (table.length - 1);268 Entry e = table[i];269 if (e != null && e.get() == key)270 return e;271 else272 return getEntryAfterMiss(key, i, e);273 }274 275 /**276 * Version of getEntry method for use when key is not found in277 * its direct hash slot.278 *279 * @param key the thread local object280 * @param i the table index for key's hash code281 * @param e the entry at table[i]282 * @return the entry associated with key, or null if no such283 */284 private Entry getEntryAfterMiss(ThreadLocal
key, int i, Entry e) {285 Entry[] tab = table;286 int len = tab.length;287 288 while (e != null) {289 ThreadLocal
k = e.get();290 if (k == key)291 return e;292 if (k == null)293 expungeStaleEntry(i);294 else295 i = nextIndex(i, len);296 e = tab[i];297 }298 return null;299 }300 301 /**302 * Set the value associated with key.303 *304 * @param key the thread local object305 * @param value the value to be set306 */307 private void set(ThreadLocal
key, Object value) {308 309 // We don't use a fast path as with get() because it is at310 // least as common to use set() to create new entries as311 // it is to replace existing ones, in which case, a fast312 // path would fail more often than not.313 314 Entry[] tab = table;315 int len = tab.length;316 int i = key.threadLocalHashCode & (len-1);317 318 for (Entry e = tab[i];319 e != null;320 e = tab[i = nextIndex(i, len)]) {321 ThreadLocal
k = e.get();322 323 if (k == key) {324 e.value = value;325 return;326 }327 328 if (k == null) {329 replaceStaleEntry(key, value, i);330 return;331 }332 }333 334 tab[i] = new Entry(key, value);335 int sz = ++size;336 if (!cleanSomeSlots(i, sz) && sz >= threshold)337 rehash();338 }339 340 /**341 * Remove the entry for key.342 */343 private void remove(ThreadLocal
key) {344 Entry[] tab = table;345 int len = tab.length;346 int i = key.threadLocalHashCode & (len-1);347 for (Entry e = tab[i];348 e != null;349 e = tab[i = nextIndex(i, len)]) {350 if (e.get() == key) {351 e.clear();352 expungeStaleEntry(i);353 return;354 }355 }356 }357 358 /**359 * Replace a stale entry encountered during a set operation360 * with an entry for the specified key. The value passed in361 * the value parameter is stored in the entry, whether or not362 * an entry already exists for the specified key.363 *364 * As a side effect, this method expunges all stale entries in the365 * "run" containing the stale entry. (A run is a sequence of entries366 * between two null slots.)367 *368 * @param key the key369 * @param value the value to be associated with key370 * @param staleSlot index of the first stale entry encountered while371 * searching for key.372 */373 private void replaceStaleEntry(ThreadLocal
key, Object value,374 int staleSlot) {375 Entry[] tab = table;376 int len = tab.length;377 Entry e;378 379 // Back up to check for prior stale entry in current run.380 // We clean out whole runs at a time to avoid continual381 // incremental rehashing due to garbage collector freeing382 // up refs in bunches (i.e., whenever the collector runs).383 int slotToExpunge = staleSlot;384 for (int i = prevIndex(staleSlot, len);385 (e = tab[i]) != null;386 i = prevIndex(i, len))387 if (e.get() == null)388 slotToExpunge = i;389 390 // Find either the key or trailing null slot of run, whichever391 // occurs first392 for (int i = nextIndex(staleSlot, len);393 (e = tab[i]) != null;394 i = nextIndex(i, len)) {395 ThreadLocal
k = e.get();396 397 // If we find key, then we need to swap it398 // with the stale entry to maintain hash table order.399 // The newly stale slot, or any other stale slot400 // encountered above it, can then be sent to expungeStaleEntry401 // to remove or rehash all of the other entries in run.402 if (k == key) {403 e.value = value;404 405 tab[i] = tab[staleSlot];406 tab[staleSlot] = e;407 408 // Start expunge at preceding stale entry if it exists409 if (slotToExpunge == staleSlot)410 slotToExpunge = i;411 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);412 return;413 }414 415 // If we didn't find stale entry on backward scan, the416 // first stale entry seen while scanning for key is the417 // first still present in the run.418 if (k == null && slotToExpunge == staleSlot)419 slotToExpunge = i;420 }421 422 // If key not found, put new entry in stale slot423 tab[staleSlot].value = null;424 tab[staleSlot] = new Entry(key, value);425 426 // If there are any other stale entries in run, expunge them427 if (slotToExpunge != staleSlot)428 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);429 }430 431 /**432 * Expunge a stale entry by rehashing any possibly colliding entries433 * lying between staleSlot and the next null slot. This also expunges434 * any other stale entries encountered before the trailing null. See435 * Knuth, Section 6.4436 *437 * @param staleSlot index of slot known to have null key438 * @return the index of the next null slot after staleSlot439 * (all between staleSlot and this slot will have been checked440 * for expunging).441 */442 private int expungeStaleEntry(int staleSlot) {443 Entry[] tab = table;444 int len = tab.length;445 446 // expunge entry at staleSlot447 tab[staleSlot].value = null;448 tab[staleSlot] = null;449 size--;450 451 // Rehash until we encounter null452 Entry e;453 int i;454 for (i = nextIndex(staleSlot, len);455 (e = tab[i]) != null;456 i = nextIndex(i, len)) {457 ThreadLocal
k = e.get();458 if (k == null) {459 e.value = null;460 tab[i] = null;461 size--;462 } else {463 int h = k.threadLocalHashCode & (len - 1);464 if (h != i) {465 tab[i] = null;466 467 // Unlike Knuth 6.4 Algorithm R, we must scan until468 // null because multiple entries could have been stale.469 while (tab[h] != null)470 h = nextIndex(h, len);471 tab[h] = e;472 }473 }474 }475 return i;476 }477 478 /**479 * Heuristically scan some cells looking for stale entries.480 * This is invoked when either a new element is added, or481 * another stale one has been expunged. It performs a482 * logarithmic number of scans, as a balance between no483 * scanning (fast but retains garbage) and a number of scans484 * proportional to number of elements, that would find all485 * garbage but would cause some insertions to take O(n) time.486 *487 * @param i a position known NOT to hold a stale entry. The488 * scan starts at the element after i.489 *490 * @param n scan control: {
@code log2(n)} cells are scanned,491 * unless a stale entry is found, in which case492 * {
@code log2(table.length)-1} additional cells are scanned.493 * When called from insertions, this parameter is the number494 * of elements, but when from replaceStaleEntry, it is the495 * table length. (Note: all this could be changed to be either496 * more or less aggressive by weighting n instead of just497 * using straight log n. But this version is simple, fast, and498 * seems to work well.)499 *500 * @return true if any stale entries have been removed.501 */502 private boolean cleanSomeSlots(int i, int n) {503 boolean removed = false;504 Entry[] tab = table;505 int len = tab.length;506 do {507 i = nextIndex(i, len);508 Entry e = tab[i];509 if (e != null && e.get() == null) {510 n = len;511 removed = true;512 i = expungeStaleEntry(i);513 }514 } while ( (n >>>= 1) != 0);515 return removed;516 }517 518 /**519 * Re-pack and/or re-size the table. First scan the entire520 * table removing stale entries. If this doesn't sufficiently521 * shrink the size of the table, double the table size.522 */523 private void rehash() {524 expungeStaleEntries();525 526 // Use lower threshold for doubling to avoid hysteresis527 if (size >= threshold - threshold / 4)528 resize();529 }530 531 /**532 * Double the capacity of the table.533 */534 private void resize() {535 Entry[] oldTab = table;536 int oldLen = oldTab.length;537 int newLen = oldLen * 2;538 Entry[] newTab = new Entry[newLen];539 int count = 0;540 541 for (int j = 0; j < oldLen; ++j) {542 Entry e = oldTab[j];543 if (e != null) {544 ThreadLocal
k = e.get();545 if (k == null) {546 e.value = null; // Help the GC547 } else {548 int h = k.threadLocalHashCode & (newLen - 1);549 while (newTab[h] != null)550 h = nextIndex(h, newLen);551 newTab[h] = e;552 count++;553 }554 }555 }556 557 setThreshold(newLen);558 size = count;559 table = newTab;560 }561 562 /**563 * Expunge all stale entries in the table.564 */565 private void expungeStaleEntries() {566 Entry[] tab = table;567 int len = tab.length;568 for (int j = 0; j < len; j++) {569 Entry e = tab[j];570 if (e != null && e.get() == null)571 expungeStaleEntry(j);572 }573 }574 }575 }
View Code

 

 

转载于:https://www.cnblogs.com/luangeng/p/5785527.html

你可能感兴趣的文章
CSS hack
查看>>
js几个小技巧和坑
查看>>
OSPF相关知识与实例配置【第一部分】
查看>>
JVM监控远程服务器
查看>>
python glob删除目录下的文件
查看>>
网络配置文件详解
查看>>
Tomcat部署Web应用的两种方式
查看>>
阅读《effective java-第17条》遇到的问题解决与分享
查看>>
什么时候用GET?什么时候用POST?
查看>>
VS上编译Lua5.1.4生成静态库
查看>>
JS常用方法函数整理
查看>>
软件工程网上的第一次作业
查看>>
AFN Post方法 设置请求头(json)
查看>>
学习C# XmlSerializer 序列化反序列化XML
查看>>
java学习笔记19(Arrays类)
查看>>
datagridview使用tooltip控件显示单元格内容
查看>>
【转】C/C++基本编程技能
查看>>
HTML5/CSS3(PrefixFree.js) 3D文字特效
查看>>
live555学习笔记6-建立RTP会话
查看>>
[选择性翻译][HDP Ambari 2.2.2安装使用说明][2]
查看>>