ThreadLocal & Thread源码解读
ThreadLocal是什么?
ThreadLocal出现在JDK-1.2版本中,在1.5的时候进行了优化,加入了泛型数据。ThreadLocal是用来保存本线程数据信息的,跟我们平常所用到的线程本地存储-TLS(Thread Local Storage)是一个概念。为解决非共享对象的多线程并发问题提供了一种新的思路,因此需要明确的是它不是用来解决共享对象的多线程的访问问题的;解决共享对象的多线程一般使用锁或者原子操作之类的,是多个线程排队操作共享对象。
ThreadLocal.set()到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,且其他线程也不能访问。
ThreadLocal.get()方法执行时,各线程从自己的Values中去取出放进去的对象,因此取出的是各自自己放入到线程中的对象。
ThreadLocal源码及实现原理:
1 public class ThreadLocal<T> { 2 5 /** 6 * Creates a new thread-local variable. 7 */ 8 public ThreadLocal() {} 9 10 /** 11 * Returns the value of this variable for the current thread. If an entry 12 * doesn't yet exist for this variable on this thread, this method will 13 * create an entry, populating the value with the result of 14 * {@link #initialValue()}. 15 * 16 * @return the current value of the variable for the calling thread. 17 */ 18 @SuppressWarnings("unchecked") 19 public T get() { 20 // Optimized for the fast path. 21 Thread currentThread = Thread.currentThread(); 22 Values values = values(currentThread); 23 if (values != null) { 24 Object[] table = values.table; 25 int index = hash & values.mask; 26 if (this.reference == table[index]) { 27 return (T) table[index + 1]; 28 } 29 } else { 30 values = initializeValues(currentThread); 31 } 32 33 return (T) values.getAfterMiss(this); 34 } 35 36 /** 37 * Provides the initial value of this variable for the current thread. 38 * The default implementation returns {@code null}. 39 * 40 * @return the initial value of the variable. 41 */ 42 protected T initialValue() { 43 return null; 44 } 45 46 /** 47 * Sets the value of this variable for the current thread. If set to 48 * {@code null}, the value will be set to null and the underlying entry will 49 * still be present. 50 * 51 * @param value the new value of the variable for the caller thread. 52 */ 53 public void set(T value) { 54 Thread currentThread = Thread.currentThread(); 55 Values values = values(currentThread); 56 if (values == null) { 57 values = initializeValues(currentThread); 58 } 59 values.put(this, value); 60 } 61 62 /** 63 * Removes the entry for this variable in the current thread. If this call 64 * is followed by a {@link #get()} before a {@link #set}, 65 * {@code #get()} will call {@link #initialValue()} and create a new 66 * entry with the resulting value. 67 * 68 * @since 1.5 69 */ 70 public void remove() { 71 Thread currentThread = Thread.currentThread(); 72 Values values = values(currentThread); 73 if (values != null) { 74 values.remove(this); 75 } 76 } 77 78 /** 79 * Creates Values instance for this thread and variable type. 80 */ 81 Values initializeValues(Thread current) { 82 return current.localValues = new Values(); 83 } 84 85 /** 86 * Gets Values instance for this thread and variable type. 87 */ 88 Values values(Thread current) { 89 return current.localValues; 90 } 91 92 /** Weak reference to this thread local instance. */ 93 private final Reference<ThreadLocal<T>> reference 94 = new WeakReference<ThreadLocal<T>>(this); 95 96 /** Hash counter. */ 97 private static AtomicInteger hashCounter = new AtomicInteger(0); 98 99 /** 100 * Internal hash. We deliberately don't bother with #hashCode(). 101 * Hashes must be even. This ensures that the result of 102 * (hash & (table.length - 1)) points to a key and not a value. 103 * 104 * We increment by Doug Lea's Magic Number(TM) (*2 since keys are in 105 * every other bucket) to help prevent clustering. 106 */ 107 private final int hash = hashCounter.getAndAdd(0x61c88647 * 2); 108 109 /** 110 * Per-thread map of ThreadLocal instances to values. 111 */ 112 static class Values { 113 114 /** 115 * Size must always be a power of 2. 116 */ 117 private static final int INITIAL_SIZE = 16; 118 119 /** 120 * Placeholder for deleted entries. 121 */ 122 private static final Object TOMBSTONE = new Object(); 123 124 /** 125 * Map entries. Contains alternating keys (ThreadLocal) and values. 126 * The length is always a power of 2. 127 */ 128 private Object[] table; 129 130 /** Used to turn hashes into indices. */ 131 private int mask; 132 133 /** Number of live entries. */ 134 private int size; 135 136 /** Number of tombstones. */ 137 private int tombstones; 138 139 /** Maximum number of live entries and tombstones. */ 140 private int maximumLoad; 141 142 /** Points to the next cell to clean up. */ 143 private int clean; 144 145 /** 146 * Constructs a new, empty instance. 147 */ 148 Values() { 149 initializeTable(INITIAL_SIZE); 150 this.size = 0; 151 this.tombstones = 0; 152 } 153 154 /** 155 * Used for InheritableThreadLocals. 156 */ 157 Values(Values fromParent) { 158 this.table = fromParent.table.clone(); 159 this.mask = fromParent.mask; 160 this.size = fromParent.size; 161 this.tombstones = fromParent.tombstones; 162 this.maximumLoad = fromParent.maximumLoad; 163 this.clean = fromParent.clean; 164 inheritValues(fromParent); 165 } 166 167 /** 168 * Inherits values from a parent thread. 169 */ 170 @SuppressWarnings({"unchecked"}) 171 private void inheritValues(Values fromParent) { 172 // Transfer values from parent to child thread. 173 Object[] table = this.table; 174 for (int i = table.length - 2; i >= 0; i -= 2) { 175 Object k = table[i]; 176 177 if (k == null || k == TOMBSTONE) { 178 // Skip this entry. 179 continue; 180 } 181 182 // The table can only contain null, tombstones and references. 183 Reference<InheritableThreadLocal<?>> reference 184 = (Reference<InheritableThreadLocal<?>>) k; 185 // Raw type enables us to pass in an Object below. 186 InheritableThreadLocal key = reference.get(); 187 if (key != null) { 188 // Replace value with filtered value. 189 // We should just let exceptions bubble out and tank 190 // the thread creation 191 table[i + 1] = key.childValue(fromParent.table[i + 1]); 192 } else { 193 // The key was reclaimed. 194 table[i] = TOMBSTONE; 195 table[i + 1] = null; 196 fromParent.table[i] = TOMBSTONE; 197 fromParent.table[i + 1] = null; 198 199 tombstones++; 200 fromParent.tombstones++; 201 202 size--; 203 fromParent.size--; 204 } 205 } 206 } 207 208 /** 209 * Creates a new, empty table with the given capacity. 210 */ 211 private void initializeTable(int capacity) { 212 this.table = new Object[capacity * 2]; 213 this.mask = table.length - 1; 214 this.clean = 0; 215 this.maximumLoad = capacity * 2 / 3; // 2/3 216 } 217 218 /** 219 * Cleans up after garbage-collected thread locals. 220 */ 221 private void cleanUp() { 222 if (rehash()) { 223 // If we rehashed, we needn't clean up (clean up happens as 224 // a side effect). 225 return; 226 } 227 228 if (size == 0) { 229 // No live entries == nothing to clean. 230 return; 231 } 232 233 // Clean log(table.length) entries picking up where we left off 234 // last time. 235 int index = clean; 236 Object[] table = this.table; 237 for (int counter = table.length; counter > 0; counter >>= 1, 238 index = next(index)) { 239 Object k = table[index]; 240 241 if (k == TOMBSTONE || k == null) { 242 continue; // on to next entry 243 } 244 245 // The table can only contain null, tombstones and references. 246 @SuppressWarnings("unchecked") 247 Reference<ThreadLocal<?>> reference 248 = (Reference<ThreadLocal<?>>) k; 249 if (reference.get() == null) { 250 // This thread local was reclaimed by the garbage collector. 251 table[index] = TOMBSTONE; 252 table[index + 1] = null; 253 tombstones++; 254 size--; 255 } 256 } 257 258 // Point cursor to next index. 259 clean = index; 260 } 261 262 /** 263 * Rehashes the table, expanding or contracting it as necessary. 264 * Gets rid of tombstones. Returns true if a rehash occurred. 265 * We must rehash every time we fill a null slot; we depend on the 266 * presence of null slots to end searches (otherwise, we'll infinitely 267 * loop). 268 */ 269 private boolean rehash() { 270 if (tombstones + size < maximumLoad) { 271 return false; 272 } 273 274 int capacity = table.length >> 1; 275 276 // Default to the same capacity. This will create a table of the 277 // same size and move over the live entries, analogous to a 278 // garbage collection. This should only happen if you churn a 279 // bunch of thread local garbage (removing and reinserting 280 // the same thread locals over and over will overwrite tombstones 281 // and not fill up the table). 282 int newCapacity = capacity; 283 284 if (size > (capacity >> 1)) { 285 // More than 1/2 filled w/ live entries. 286 // Double size. 287 newCapacity = capacity * 2; 288 } 289 290 Object[] oldTable = this.table; 291 292 // Allocate new table. 293 initializeTable(newCapacity); 294 295 // We won't have any tombstones after this. 296 this.tombstones = 0; 297 298 // If we have no live entries, we can quit here. 299 if (size == 0) { 300 return true; 301 } 302 303 // Move over entries. 304 for (int i = oldTable.length - 2; i >= 0; i -= 2) { 305 Object k = oldTable[i]; 306 if (k == null || k == TOMBSTONE) { 307 // Skip this entry. 308 continue; 309 } 310 311 // The table can only contain null, tombstones and references. 312 @SuppressWarnings("unchecked") 313 Reference<ThreadLocal<?>> reference 314 = (Reference<ThreadLocal<?>>) k; 315 ThreadLocal<?> key = reference.get(); 316 if (key != null) { 317 // Entry is still live. Move it over. 318 add(key, oldTable[i + 1]); 319 } else { 320 // The key was reclaimed. 321 size--; 322 } 323 } 324 325 return true; 326 } 327 328 /** 329 * Adds an entry during rehashing. Compared to put(), this method 330 * doesn't have to clean up, check for existing entries, account for 331 * tombstones, etc. 332 */ 333 void add(ThreadLocal<?> key, Object value) { 334 for (int index = key.hash & mask;; index = next(index)) { 335 Object k = table[index]; 336 if (k == null) { 337 table[index] = key.reference; 338 table[index + 1] = value; 339 return; 340 } 341 } 342 } 343 344 /** 345 * Sets entry for given ThreadLocal to given value, creating an 346 * entry if necessary. 347 */ 348 void put(ThreadLocal<?> key, Object value) { 349 cleanUp(); 350 351 // Keep track of first tombstone. That's where we want to go back 352 // and add an entry if necessary. 353 int firstTombstone = -1; 354 355 for (int index = key.hash & mask;; index = next(index)) { 356 Object k = table[index]; 357 358 if (k == key.reference) { 359 // Replace existing entry. 360 table[index + 1] = value; 361 return; 362 } 363 364 if (k == null) { 365 if (firstTombstone == -1) { 366 // Fill in null slot. 367 table[index] = key.reference; 368 table[index + 1] = value; 369 size++; 370 return; 371 } 372 373 // Go back and replace first tombstone. 374 table[firstTombstone] = key.reference; 375 table[firstTombstone + 1] = value; 376 tombstones--; 377 size++; 378 return; 379 } 380 381 // Remember first tombstone. 382 if (firstTombstone == -1 && k == TOMBSTONE) { 383 firstTombstone = index; 384 } 385 } 386 } 387 388 /** 389 * Gets value for given ThreadLocal after not finding it in the first 390 * slot. 391 */ 392 Object getAfterMiss(ThreadLocal<?> key) { 393 Object[] table = this.table; 394 int index = key.hash & mask; 395 396 // If the first slot is empty, the search is over. 397 if (table[index] == null) { 398 Object value = key.initialValue(); 399 400 // If the table is still the same and the slot is still empty... 401 if (this.table == table && table[index] == null) { 402 table[index] = key.reference; 403 table[index + 1] = value; 404 size++; 405 406 cleanUp(); 407 return value; 408 } 409 410 // The table changed during initialValue(). 411 put(key, value); 412 return value; 413 } 414 415 // Keep track of first tombstone. That's where we want to go back 416 // and add an entry if necessary. 417 int firstTombstone = -1; 418 419 // Continue search. 420 for (index = next(index);; index = next(index)) { 421 Object reference = table[index]; 422 if (reference == key.reference) { 423 return table[index + 1]; 424 } 425 426 // If no entry was found... 427 if (reference == null) { 428 Object value = key.initialValue(); 429 430 // If the table is still the same... 431 if (this.table == table) { 432 // If we passed a tombstone and that slot still 433 // contains a tombstone... 434 if (firstTombstone > -1 435 && table[firstTombstone] == TOMBSTONE) { 436 table[firstTombstone] = key.reference; 437 table[firstTombstone + 1] = value; 438 tombstones--; 439 size++; 440 441 // No need to clean up here. We aren't filling 442 // in a null slot. 443 return value; 444 } 445 446 // If this slot is still empty... 447 if (table[index] == null) { 448 table[index] = key.reference; 449 table[index + 1] = value; 450 size++; 451 452 cleanUp(); 453 return value; 454 } 455 } 456 457 // The table changed during initialValue(). 458 put(key, value); 459 return value; 460 } 461 462 if (firstTombstone == -1 && reference == TOMBSTONE) { 463 // Keep track of this tombstone so we can overwrite it. 464 firstTombstone = index; 465 } 466 } 467 } 468 469 /** 470 * Removes entry for the given ThreadLocal. 471 */ 472 void remove(ThreadLocal<?> key) { 473 cleanUp(); 474 475 for (int index = key.hash & mask;; index = next(index)) { 476 Object reference = table[index]; 477 478 if (reference == key.reference) { 479 // Success! 480 table[index] = TOMBSTONE; 481 table[index + 1] = null; 482 tombstones++; 483 size--; 484 return; 485 } 486 487 if (reference == null) { 488 // No entry found. 489 return; 490 } 491 } 492 } 493 494 /** 495 * Gets the next index. If we're at the end of the table, we wrap back 496 * around to 0. 497 */ 498 private int next(int index) { 499 return (index + 2) & mask; 500 } 501 } 502 }
主要用到的两个方法:
public void set(T value);
public T get()
首先从set方法说起,相关代码如下:
1. 获取到当前的线程;
2. 获取当前线程的ThreadLocal的localValues值;localValues中保存一个数组,数组的索引是通过传入的hash值及数组的大小与运算而计算出来的。
3. 如果获取到的values值为空,调用initialzeValues(...)方法,此方法只有一句代码,current.localValues = new Values();
4. 存入值到values中去。put方法中做了很多少事情,需要判断是否要rehash等,
1 public void set(T value) { 2 Thread currentThread = Thread.currentThread(); 3 Values values = values(currentThread); 4 if (values == null) { 5 values = initializeValues(currentThread); 6 } 7 values.put(this, value); 8 }
get方法实现:
1. 前面两行代码同set方法,获取当前线程,并获取到当前线程的localValues对象。
2. 如果value不为空,则从数组中获取数据,如果当前数组保存的数据是当前线程中的ThreadLocal的引用,则返回table[index+1];
3. 如果values为空,先new 一个对象,返回values.getAfterMiss(this);
1 public T get() { 2 // Optimized for the fast path. 3 Thread currentThread = Thread.currentThread(); 4 Values values = values(currentThread); 5 if (values != null) { 6 Object[] table = values.table; 7 int index = hash & values.mask; 8 if (this.reference == table[index]) { 9 return (T) table[index + 1]; 10 } 11 } else { 12 values = initializeValues(currentThread); 13 } 14 15 return (T) values.getAfterMiss(this); 16 }
总结:
1. Values中的table数组必须是2的幂次方,偶数地址上保存的reference,奇数地址上保存的是需要保存的值。
2. ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象
浙公网安备 33010602011771号