线程系列3---ThreadLocal类研究

2013-12-23 17:44:44

Java为线程安全提供了一些工具类,如ThreadLocal类,它代表一个线程局部变量,通过把数据放在ThreadLocal中就可以让每个线程创建一个该变量的副本,从而避免并发访问的线程安全问题。

线程局部变量的功能其实很简单,就是为每一个使用该变量的线程提供一个副本,使每一个线程都可以独立的访问属于自己的副本,而不会和其他线程的副本产生冲突,就好像每一个线程都完全拥有该变量一样。

ThreadLocal类并不能替代同步机制,两者面向的问题领域不同。

同步机制是为了同步多个线程对相同资源的并发访问,是多个线程之间进行通信的有效方式;

ThreadLocal是为了隔离多个线程的数据共享,从根本上避免多个线程对共享资源的竞争,也就不需要对多个线程进行同步了。

自己做了一个Demo, 如下:

ThreadLocalTest.java

 1 package thread;

 2 

 3 public class ThreadLocalTest {

 4 

 5     public ThreadLocal<String> local = new ThreadLocal<String>();

 6 

 7     private String name;

 8 

 9     public ThreadLocalTest() {

10     }

11 

12     public String getName() {

13         // return name;

14         return local.get();

15     }

16 

17     public void setName(String name) {

18         // this.name = name;

19         local.set(name);

20     }

21 

22     public static void main(String[] args) {

23         ThreadLocalTest one = new ThreadLocalTest();

24         ThreadOne t1 = new ThreadOne(one);

25         ThreadTwo t2 = new ThreadTwo(one);

26         t1.start();

27         t2.start();

28     }

29 }

ThreadOne.java

 1 package thread;

 2 

 3 public class ThreadOne extends Thread {

 4     ThreadLocalTest one;

 5 

 6     public ThreadOne(ThreadLocalTest o) {

 7         one = o;

 8     }

 9 

10     @Override

11     public void run() {

12         for (int i = 0; i < 10; i++) {

13             StringBuilder ss = new StringBuilder("      one_");

14             ss.append(i);

15             one.setName(ss.toString());

16             try {

17                 Thread.sleep(280);

18             } catch (InterruptedException e) {

19                 e.printStackTrace();

20             }

21             System.out.println("线程一:" + Thread.currentThread().getName()

22                     + one.getName());

23         }

24     }

25 }

ThreadTwo.java

 1 package thread;

 2 

 3 public class ThreadTwo extends Thread {

 4     ThreadLocalTest one;

 5 

 6     public ThreadTwo(ThreadLocalTest o) {

 7         one = o;

 8     }

 9 

10     @Override

11     public void run() {

12         for (int i = 0; i < 10; i++) {

13             StringBuilder ss = new StringBuilder("------------two_");

14             ss.append(i);

15             one.setName(ss.toString());

16             try {

17                 Thread.sleep(100);

18             } catch (InterruptedException e) {

19                 e.printStackTrace();

20             }

21             System.out.println("线程二:" + Thread.currentThread().getName()

22                     + one.getName());

23         }

24     }

25 }

先看不是用ThreadLocal的log(使用ThreadLocalTest.java中注释掉的部分),如下:

 1 线程二:Thread-1------------two_0

 2 线程二:Thread-1------------two_1

 3 线程一:Thread-0------------two_2

 4 线程二:Thread-1      one_1

 5 线程二:Thread-1------------two_3

 6 线程二:Thread-1------------two_4

 7 线程一:Thread-0------------two_5

 8 线程二:Thread-1      one_2

 9 线程二:Thread-1------------two_6

10 线程二:Thread-1------------two_7

11 线程一:Thread-0------------two_8

12 线程二:Thread-1      one_3

13 线程二:Thread-1------------two_9

14 线程一:Thread-0------------two_9

15 线程一:Thread-0      one_4

16 线程一:Thread-0      one_5

17 线程一:Thread-0      one_6

18 线程一:Thread-0      one_7

19 线程一:Thread-0      one_8

20 线程一:Thread-0      one_9

可以上面红色部分,可以发现就是多线程导致资源混乱的情况。

下面是使用了ThreadLocal后的log:

 1 线程二:Thread-1------------two_0

 2 线程二:Thread-1------------two_1

 3 线程一:Thread-0      one_0

 4 线程二:Thread-1------------two_2

 5 线程二:Thread-1------------two_3

 6 线程二:Thread-1------------two_4

 7 线程一:Thread-0      one_1

 8 线程二:Thread-1------------two_5

 9 线程二:Thread-1------------two_6

10 线程二:Thread-1------------two_7

11 线程一:Thread-0      one_2

12 线程二:Thread-1------------two_8

13 线程二:Thread-1------------two_9

14 线程一:Thread-0      one_3

15 线程一:Thread-0      one_4

16 线程一:Thread-0      one_5

17 线程一:Thread-0      one_6

18 线程一:Thread-0      one_7

19 线程一:Thread-0      one_8

20 线程一:Thread-0      one_9

 一切正常。

但是我发先了一个问题,那就是ThreadLocal类在Android和Java中的实现不太一样,把它们的源码都贴出来,有兴趣的同学可以好好研究一下:

Java SE-1.6

  1 /*

  2  * %W% %E%

  3  *

  4  * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.

  5  * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.

  6  */

  7 

  8 package java.lang;

  9 import java.lang.ref.*;

 10 import java.util.concurrent.atomic.AtomicInteger;

 11 

 12 /**

 13  * This class provides thread-local variables.  These variables differ from

 14  * their normal counterparts in that each thread that accesses one (via its

 15  * <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized

 16  * copy of the variable.  <tt>ThreadLocal</tt> instances are typically private

 17  * static fields in classes that wish to associate state with a thread (e.g.,

 18  * a user ID or Transaction ID).

 19  *

 20  * <p>For example, the class below generates unique identifiers local to each

 21  * thread.

 22  * A thread's id is

 23  * assigned the first time it invokes <tt>UniqueThreadIdGenerator.getCurrentThreadId()</tt> and remains unchanged on subsequent calls.

 24  * <pre>

 25  * import java.util.concurrent.atomic.AtomicInteger;

 26  *

 27  * public class UniqueThreadIdGenerator {

 28  *

 29  *     private static final AtomicInteger uniqueId = new AtomicInteger(0);

 30  *

 31  *     private static final ThreadLocal &lt; Integer > uniqueNum = 

 32  *         new ThreadLocal &lt; Integer > () {

 33  *             &#64;Override protected Integer initialValue() {

 34  *                 return uniqueId.getAndIncrement();

 35  *         }

 36  *     };

 37  * 

 38  *     public static int getCurrentThreadId() {

 39  *         return uniqueId.get();

 40  *     }

 41  * } // UniqueThreadIdGenerator

 42  * </pre>

 43  * <p>Each thread holds an implicit reference to its copy of a thread-local

 44  * variable as long as the thread is alive and the <tt>ThreadLocal</tt>

 45  * instance is accessible; after a thread goes away, all of its copies of

 46  * thread-local instances are subject to garbage collection (unless other

 47  * references to these copies exist). 

 48  *

 49  * @author  Josh Bloch and Doug Lea

 50  * @version %I%, %G%

 51  * @since   1.2

 52  */

 53 public class ThreadLocal<T> {

 54     /**

 55      * ThreadLocals rely on per-thread linear-probe hash maps attached

 56      * to each thread (Thread.threadLocals and

 57      * inheritableThreadLocals).  The ThreadLocal objects act as keys,

 58      * searched via threadLocalHashCode.  This is a custom hash code

 59      * (useful only within ThreadLocalMaps) that eliminates collisions

 60      * in the common case where consecutively constructed ThreadLocals

 61      * are used by the same threads, while remaining well-behaved in

 62      * less common cases.

 63      */

 64     private final int threadLocalHashCode = nextHashCode();

 65 

 66     /**

 67      * The next hash code to be given out. Updated atomically. Starts at

 68      * zero.

 69      */

 70     private static AtomicInteger nextHashCode = 

 71     new AtomicInteger();

 72 

 73     /**

 74      * The difference between successively generated hash codes - turns

 75      * implicit sequential thread-local IDs into near-optimally spread

 76      * multiplicative hash values for power-of-two-sized tables.

 77      */

 78     private static final int HASH_INCREMENT = 0x61c88647;

 79 

 80     /**

 81      * Returns the next hash code.

 82      */

 83     private static int nextHashCode() {

 84     return nextHashCode.getAndAdd(HASH_INCREMENT); 

 85     }

 86 

 87     /**

 88      * Returns the current thread's "initial value" for this

 89      * thread-local variable.  This method will be invoked the first

 90      * time a thread accesses the variable with the {@link #get}

 91      * method, unless the thread previously invoked the {@link #set}

 92      * method, in which case the <tt>initialValue</tt> method will not

 93      * be invoked for the thread.  Normally, this method is invoked at

 94      * most once per thread, but it may be invoked again in case of

 95      * subsequent invocations of {@link #remove} followed by {@link #get}.

 96      *

 97      * <p>This implementation simply returns <tt>null</tt>; if the

 98      * programmer desires thread-local variables to have an initial

 99      * value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be

100      * subclassed, and this method overridden.  Typically, an

101      * anonymous inner class will be used.

102      *

103      * @return the initial value for this thread-local

104      */

105     protected T initialValue() {

106         return null;

107     }

108 

109     /**

110      * Creates a thread local variable.

111      */

112     public ThreadLocal() {

113     }

114 

115     /**

116      * Returns the value in the current thread's copy of this

117      * thread-local variable.  If the variable has no value for the

118      * current thread, it is first initialized to the value returned

119      * by an invocation of the {@link #initialValue} method.

120      *

121      * @return the current thread's value of this thread-local

122      */

123     public T get() {

124         Thread t = Thread.currentThread();

125         ThreadLocalMap map = getMap(t);

126         if (map != null) {

127             ThreadLocalMap.Entry e = map.getEntry(this);

128             if (e != null)

129                 return (T)e.value;

130         }

131         return setInitialValue();

132     }

133 

134     /**

135      * Variant of set() to establish initialValue. Used instead

136      * of set() in case user has overridden the set() method.

137      *

138      * @return the initial value

139      */

140     private T setInitialValue() {

141         T value = initialValue();

142         Thread t = Thread.currentThread();

143         ThreadLocalMap map = getMap(t);

144         if (map != null)

145             map.set(this, value);

146         else

147             createMap(t, value);

148         return value;

149     }

150 

151     /**

152      * Sets the current thread's copy of this thread-local variable

153      * to the specified value.  Most subclasses will have no need to 

154      * override this method, relying solely on the {@link #initialValue}

155      * method to set the values of thread-locals.

156      *

157      * @param value the value to be stored in the current thread's copy of

158      *        this thread-local.

159      */

160     public void set(T value) {

161         Thread t = Thread.currentThread();

162         ThreadLocalMap map = getMap(t);

163         if (map != null)

164             map.set(this, value);

165         else

166             createMap(t, value);

167     }

168 

169     /**

170      * Removes the current thread's value for this thread-local

171      * variable.  If this thread-local variable is subsequently

172      * {@linkplain #get read} by the current thread, its value will be

173      * reinitialized by invoking its {@link #initialValue} method,

174      * unless its value is {@linkplain #set set} by the current thread

175      * in the interim.  This may result in multiple invocations of the

176      * <tt>initialValue</tt> method in the current thread.

177      *

178      * @since 1.5

179      */

180      public void remove() {

181          ThreadLocalMap m = getMap(Thread.currentThread());

182          if (m != null)

183              m.remove(this);

184      }

185 

186     /**

187      * Get the map associated with a ThreadLocal. Overridden in

188      * InheritableThreadLocal.

189      *

190      * @param  t the current thread

191      * @return the map

192      */

193     ThreadLocalMap getMap(Thread t) {

194         return t.threadLocals;

195     }

196 

197     /**

198      * Create the map associated with a ThreadLocal. Overridden in

199      * InheritableThreadLocal.

200      *

201      * @param t the current thread

202      * @param firstValue value for the initial entry of the map

203      * @param map the map to store.

204      */

205     void createMap(Thread t, T firstValue) {

206         t.threadLocals = new ThreadLocalMap(this, firstValue);

207     }

208 

209     /**

210      * Factory method to create map of inherited thread locals.

211      * Designed to be called only from Thread constructor.

212      *

213      * @param  parentMap the map associated with parent thread

214      * @return a map containing the parent's inheritable bindings

215      */

216     static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {

217         return new ThreadLocalMap(parentMap);

218     }

219 

220     /**

221      * Method childValue is visibly defined in subclass

222      * InheritableThreadLocal, but is internally defined here for the

223      * sake of providing createInheritedMap factory method without

224      * needing to subclass the map class in InheritableThreadLocal.

225      * This technique is preferable to the alternative of embedding

226      * instanceof tests in methods.

227      */

228     T childValue(T parentValue) {

229         throw new UnsupportedOperationException();

230     }

231 

232     /**

233      * ThreadLocalMap is a customized hash map suitable only for

234      * maintaining thread local values. No operations are exported

235      * outside of the ThreadLocal class. The class is package private to

236      * allow declaration of fields in class Thread.  To help deal with

237      * very large and long-lived usages, the hash table entries use

238      * WeakReferences for keys. However, since reference queues are not

239      * used, stale entries are guaranteed to be removed only when

240      * the table starts running out of space.

241      */

242     static class ThreadLocalMap {

243 

244         /**

245          * The entries in this hash map extend WeakReference, using

246          * its main ref field as the key (which is always a

247          * ThreadLocal object).  Note that null keys (i.e. entry.get()

248          * == null) mean that the key is no longer referenced, so the

249          * entry can be expunged from table.  Such entries are referred to

250          * as "stale entries" in the code that follows.

251          */

252         static class Entry extends WeakReference<ThreadLocal> {

253             /** The value associated with this ThreadLocal. */

254             Object value;

255 

256             Entry(ThreadLocal k, Object v) {

257                 super(k);

258                 value = v;

259             }

260         }

261 

262         /**

263          * The initial capacity -- MUST be a power of two.

264          */

265         private static final int INITIAL_CAPACITY = 16;

266 

267         /**

268          * The table, resized as necessary.

269          * table.length MUST always be a power of two.

270          */

271         private Entry[] table;

272 

273         /**

274          * The number of entries in the table.

275          */

276         private int size = 0;

277 

278         /**

279          * The next size value at which to resize.

280          */

281         private int threshold; // Default to 0

282 

283         /**

284          * Set the resize threshold to maintain at worst a 2/3 load factor.

285          */

286         private void setThreshold(int len) {

287             threshold = len * 2 / 3;

288         }

289 

290         /**

291          * Increment i modulo len.

292          */

293         private static int nextIndex(int i, int len) {

294             return ((i + 1 < len) ? i + 1 : 0);

295         }

296 

297         /**

298          * Decrement i modulo len.

299          */

300         private static int prevIndex(int i, int len) {

301             return ((i - 1 >= 0) ? i - 1 : len - 1);

302         }

303 

304         /**

305          * Construct a new map initially containing (firstKey, firstValue).

306          * ThreadLocalMaps are constructed lazily, so we only create

307          * one when we have at least one entry to put in it.

308          */

309         ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {

310             table = new Entry[INITIAL_CAPACITY];

311             int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);

312             table[i] = new Entry(firstKey, firstValue);

313             size = 1;

314             setThreshold(INITIAL_CAPACITY);

315         }

316 

317         /**

318          * Construct a new map including all Inheritable ThreadLocals

319          * from given parent map. Called only by createInheritedMap.

320          *

321          * @param parentMap the map associated with parent thread.

322          */

323         private ThreadLocalMap(ThreadLocalMap parentMap) {

324             Entry[] parentTable = parentMap.table;

325             int len = parentTable.length;

326             setThreshold(len);

327             table = new Entry[len];

328 

329             for (int j = 0; j < len; j++) {

330                 Entry e = parentTable[j];

331                 if (e != null) {

332                     ThreadLocal key = e.get();

333                     if (key != null) {

334                         Object value = key.childValue(e.value);

335                         Entry c = new Entry(key, value);

336                         int h = key.threadLocalHashCode & (len - 1);

337                         while (table[h] != null)

338                             h = nextIndex(h, len);

339                         table[h] = c;

340                         size++;

341                     }

342                 }

343             }

344         }

345 

346         /**

347          * Get the entry associated with key.  This method

348          * itself handles only the fast path: a direct hit of existing

349          * key. It otherwise relays to getEntryAfterMiss.  This is

350          * designed to maximize performance for direct hits, in part

351          * by making this method readily inlinable.

352          *

353          * @param  key the thread local object

354          * @return the entry associated with key, or null if no such

355          */

356         private Entry getEntry(ThreadLocal key) {

357             int i = key.threadLocalHashCode & (table.length - 1);

358             Entry e = table[i];

359             if (e != null && e.get() == key)

360                 return e;

361             else

362                 return getEntryAfterMiss(key, i, e);

363         }

364 

365         /**

366          * Version of getEntry method for use when key is not found in

367          * its direct hash slot.

368          *

369          * @param  key the thread local object

370          * @param  i the table index for key's hash code

371          * @param  e the entry at table[i]

372          * @return the entry associated with key, or null if no such

373          */

374         private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {

375             Entry[] tab = table;

376             int len = tab.length;

377 

378             while (e != null) {

379                 ThreadLocal k = e.get();

380                 if (k == key)

381                     return e;

382                 if (k == null)

383                     expungeStaleEntry(i);

384                 else

385                     i = nextIndex(i, len);

386                 e = tab[i];

387             }

388             return null;

389         }

390 

391         /**

392          * Set the value associated with key.

393          *

394          * @param key the thread local object

395          * @param value the value to be set

396          */

397         private void set(ThreadLocal key, Object value) {

398 

399             // We don't use a fast path as with get() because it is at

400             // least as common to use set() to create new entries as

401             // it is to replace existing ones, in which case, a fast

402             // path would fail more often than not.

403 

404             Entry[] tab = table;

405             int len = tab.length;

406             int i = key.threadLocalHashCode & (len-1);

407 

408             for (Entry e = tab[i];

409          e != null;

410          e = tab[i = nextIndex(i, len)]) {

411                 ThreadLocal k = e.get();

412 

413                 if (k == key) {

414                     e.value = value;

415                     return;

416                 }

417 

418                 if (k == null) {

419                     replaceStaleEntry(key, value, i);

420                     return;

421                 }

422             }

423 

424             tab[i] = new Entry(key, value);

425             int sz = ++size;

426             if (!cleanSomeSlots(i, sz) && sz >= threshold)

427                 rehash();

428         }

429 

430         /**

431          * Remove the entry for key.

432          */

433         private void remove(ThreadLocal key) {

434             Entry[] tab = table;

435             int len = tab.length;

436             int i = key.threadLocalHashCode & (len-1);

437             for (Entry e = tab[i];

438          e != null;

439          e = tab[i = nextIndex(i, len)]) {

440                 if (e.get() == key) {

441                     e.clear();

442                     expungeStaleEntry(i);

443                     return;

444                 }

445             }

446         }

447 

448         /**

449          * Replace a stale entry encountered during a set operation

450          * with an entry for the specified key.  The value passed in

451          * the value parameter is stored in the entry, whether or not

452          * an entry already exists for the specified key.

453          *

454          * As a side effect, this method expunges all stale entries in the

455          * "run" containing the stale entry.  (A run is a sequence of entries

456          * between two null slots.)

457          *

458          * @param  key the key

459          * @param  value the value to be associated with key

460          * @param  staleSlot index of the first stale entry encountered while

461          *         searching for key.

462          */

463         private void replaceStaleEntry(ThreadLocal key, Object value,

464                                        int staleSlot) {

465             Entry[] tab = table;

466             int len = tab.length;

467             Entry e;

468 

469             // Back up to check for prior stale entry in current run.

470             // We clean out whole runs at a time to avoid continual

471             // incremental rehashing due to garbage collector freeing

472             // up refs in bunches (i.e., whenever the collector runs).

473             int slotToExpunge = staleSlot;

474             for (int i = prevIndex(staleSlot, len);

475          (e = tab[i]) != null;

476                  i = prevIndex(i, len))

477                 if (e.get() == null)

478                     slotToExpunge = i;

479 

480             // Find either the key or trailing null slot of run, whichever

481             // occurs first

482             for (int i = nextIndex(staleSlot, len);

483          (e = tab[i]) != null;

484                  i = nextIndex(i, len)) {

485                 ThreadLocal k = e.get();

486 

487                 // If we find key, then we need to swap it

488                 // with the stale entry to maintain hash table order.

489                 // The newly stale slot, or any other stale slot

490                 // encountered above it, can then be sent to expungeStaleEntry

491                 // to remove or rehash all of the other entries in run.

492                 if (k == key) {

493                     e.value = value;

494 

495                     tab[i] = tab[staleSlot];

496                     tab[staleSlot] = e;

497 

498                     // Start expunge at preceding stale entry if it exists

499                     if (slotToExpunge == staleSlot)

500                         slotToExpunge = i;

501                     cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);

502                     return;

503                 }

504 

505                 // If we didn't find stale entry on backward scan, the

506                 // first stale entry seen while scanning for key is the

507                 // first still present in the run.

508                 if (k == null && slotToExpunge == staleSlot)

509                     slotToExpunge = i;

510             }

511 

512             // If key not found, put new entry in stale slot

513             tab[staleSlot].value = null;   

514             tab[staleSlot] = new Entry(key, value);

515 

516             // If there are any other stale entries in run, expunge them

517             if (slotToExpunge != staleSlot)

518                 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);

519         }

520 

521         /**

522          * Expunge a stale entry by rehashing any possibly colliding entries

523          * lying between staleSlot and the next null slot.  This also expunges

524          * any other stale entries encountered before the trailing null.  See

525          * Knuth, Section 6.4

526          *

527          * @param staleSlot index of slot known to have null key

528          * @return the index of the next null slot after staleSlot

529          * (all between staleSlot and this slot will have been checked

530          * for expunging).

531          */

532         private int expungeStaleEntry(int staleSlot) {

533             Entry[] tab = table;

534             int len = tab.length;

535 

536             // expunge entry at staleSlot

537             tab[staleSlot].value = null;   

538             tab[staleSlot] = null;

539             size--;

540 

541             // Rehash until we encounter null

542             Entry e;

543             int i;

544             for (i = nextIndex(staleSlot, len);

545          (e = tab[i]) != null;

546                  i = nextIndex(i, len)) {

547                 ThreadLocal k = e.get();

548                 if (k == null) {

549                     e.value = null;

550                     tab[i] = null;

551                     size--;

552                 } else {

553                     int h = k.threadLocalHashCode & (len - 1);

554                     if (h != i) {

555                         tab[i] = null;

556 

557                         // Unlike Knuth 6.4 Algorithm R, we must scan until

558                         // null because multiple entries could have been stale.

559                         while (tab[h] != null)

560                             h = nextIndex(h, len);

561                         tab[h] = e;

562                     }

563                 }

564             }

565             return i;

566         }

567 

568         /**

569          * Heuristically scan some cells looking for stale entries.

570          * This is invoked when either a new element is added, or

571          * another stale one has been expunged. It performs a

572          * logarithmic number of scans, as a balance between no

573          * scanning (fast but retains garbage) and a number of scans

574          * proportional to number of elements, that would find all

575          * garbage but would cause some insertions to take O(n) time.

576          *

577          * @param i a position known NOT to hold a stale entry. The

578          * scan starts at the element after i.

579          *

580          * @param n scan control: <tt>log2(n)</tt> cells are scanned,

581          * unless a stale entry is found, in which case

582          * <tt>log2(table.length)-1</tt> additional cells are scanned.

583          * When called from insertions, this parameter is the number

584          * of elements, but when from replaceStaleEntry, it is the

585          * table length. (Note: all this could be changed to be either

586          * more or less aggressive by weighting n instead of just

587          * using straight log n. But this version is simple, fast, and

588          * seems to work well.)

589          *

590          * @return true if any stale entries have been removed.

591          */

592         private boolean cleanSomeSlots(int i, int n) {

593             boolean removed = false;

594             Entry[] tab = table;

595             int len = tab.length;

596             do {

597                 i = nextIndex(i, len);

598                 Entry e = tab[i];

599                 if (e != null && e.get() == null) {

600                     n = len;

601                     removed = true;

602                     i = expungeStaleEntry(i);

603                 }

604             } while ( (n >>>= 1) != 0);

605             return removed;

606         }

607 

608         /**

609          * Re-pack and/or re-size the table. First scan the entire

610          * table removing stale entries. If this doesn't sufficiently

611          * shrink the size of the table, double the table size.

612          */

613         private void rehash() {

614             expungeStaleEntries();

615 

616             // Use lower threshold for doubling to avoid hysteresis

617             if (size >= threshold - threshold / 4)

618                 resize();

619         }

620 

621         /**

622          * Double the capacity of the table.

623          */

624         private void resize() {

625             Entry[] oldTab = table;

626             int oldLen = oldTab.length;

627             int newLen = oldLen * 2;

628             Entry[] newTab = new Entry[newLen];

629             int count = 0;

630 

631             for (int j = 0; j < oldLen; ++j) {

632                 Entry e = oldTab[j];

633                 if (e != null) {

634                     ThreadLocal k = e.get();

635                     if (k == null) {

636                         e.value = null; // Help the GC

637                     } else {

638                         int h = k.threadLocalHashCode & (newLen - 1);

639                         while (newTab[h] != null)

640                             h = nextIndex(h, newLen);

641                         newTab[h] = e;

642                         count++;

643                     }

644                 }

645             }

646 

647             setThreshold(newLen);

648             size = count;

649             table = newTab;

650         }

651 

652         /**

653          * Expunge all stale entries in the table.

654          */

655         private void expungeStaleEntries() {

656             Entry[] tab = table;

657             int len = tab.length;

658             for (int j = 0; j < len; j++) {

659                 Entry e = tab[j];

660                 if (e != null && e.get() == null)

661                     expungeStaleEntry(j);

662             }

663         }

664     }

665 }
View Code

Android 4.4

  1 /*

  2  * Copyright (C) 2008 The Android Open Source Project

  3  *

  4  * Licensed under the Apache License, Version 2.0 (the "License");

  5  * you may not use this file except in compliance with the License.

  6  * You may obtain a copy of the License at

  7  *

  8  *      http://www.apache.org/licenses/LICENSE-2.0

  9  *

 10  * Unless required by applicable law or agreed to in writing, software

 11  * distributed under the License is distributed on an "AS IS" BASIS,

 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 13  * See the License for the specific language governing permissions and

 14  * limitations under the License.

 15  */

 16 

 17 package java.lang;

 18 

 19 import java.lang.ref.Reference;

 20 import java.lang.ref.WeakReference;

 21 import java.util.concurrent.atomic.AtomicInteger;

 22 

 23 /**

 24  * Implements a thread-local storage, that is, a variable for which each thread

 25  * has its own value. All threads share the same {@code ThreadLocal} object,

 26  * but each sees a different value when accessing it, and changes made by one

 27  * thread do not affect the other threads. The implementation supports

 28  * {@code null} values.

 29  *

 30  * @see java.lang.Thread

 31  * @author Bob Lee

 32  */

 33 public class ThreadLocal<T> {

 34 

 35     /* Thanks to Josh Bloch and Doug Lea for code reviews and impl advice. */

 36 

 37     /**

 38      * Creates a new thread-local variable.

 39      */

 40     public ThreadLocal() {}

 41 

 42     /**

 43      * Returns the value of this variable for the current thread. If an entry

 44      * doesn't yet exist for this variable on this thread, this method will

 45      * create an entry, populating the value with the result of

 46      * {@link #initialValue()}.

 47      *

 48      * @return the current value of the variable for the calling thread.

 49      */

 50     @SuppressWarnings("unchecked")

 51     public T get() {

 52         // Optimized for the fast path.

 53         Thread currentThread = Thread.currentThread();

 54         Values values = values(currentThread);

 55         if (values != null) {

 56             Object[] table = values.table;

 57             int index = hash & values.mask;

 58             if (this.reference == table[index]) {

 59                 return (T) table[index + 1];

 60             }

 61         } else {

 62             values = initializeValues(currentThread);

 63         }

 64 

 65         return (T) values.getAfterMiss(this);

 66     }

 67 

 68     /**

 69      * Provides the initial value of this variable for the current thread.

 70      * The default implementation returns {@code null}.

 71      *

 72      * @return the initial value of the variable.

 73      */

 74     protected T initialValue() {

 75         return null;

 76     }

 77 

 78     /**

 79      * Sets the value of this variable for the current thread. If set to

 80      * {@code null}, the value will be set to null and the underlying entry will

 81      * still be present.

 82      *

 83      * @param value the new value of the variable for the caller thread.

 84      */

 85     public void set(T value) {

 86         Thread currentThread = Thread.currentThread();

 87         Values values = values(currentThread);

 88         if (values == null) {

 89             values = initializeValues(currentThread);

 90         }

 91         values.put(this, value);

 92     }

 93 

 94     /**

 95      * Removes the entry for this variable in the current thread. If this call

 96      * is followed by a {@link #get()} before a {@link #set},

 97      * {@code #get()} will call {@link #initialValue()} and create a new

 98      * entry with the resulting value.

 99      *

100      * @since 1.5

101      */

102     public void remove() {

103         Thread currentThread = Thread.currentThread();

104         Values values = values(currentThread);

105         if (values != null) {

106             values.remove(this);

107         }

108     }

109 

110     /**

111      * Creates Values instance for this thread and variable type.

112      */

113     Values initializeValues(Thread current) {

114         return current.localValues = new Values();

115     }

116 

117     /**

118      * Gets Values instance for this thread and variable type.

119      */

120     Values values(Thread current) {

121         return current.localValues;

122     }

123 

124     /** Weak reference to this thread local instance. */

125     private final Reference<ThreadLocal<T>> reference

126             = new WeakReference<ThreadLocal<T>>(this);

127 

128     /** Hash counter. */

129     private static AtomicInteger hashCounter = new AtomicInteger(0);

130 

131     /**

132      * Internal hash. We deliberately don't bother with #hashCode().

133      * Hashes must be even. This ensures that the result of

134      * (hash & (table.length - 1)) points to a key and not a value.

135      *

136      * We increment by Doug Lea's Magic Number(TM) (*2 since keys are in

137      * every other bucket) to help prevent clustering.

138      */

139     private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);

140 

141     /**

142      * Per-thread map of ThreadLocal instances to values.

143      */

144     static class Values {

145 

146         /**

147          * Size must always be a power of 2.

148          */

149         private static final int INITIAL_SIZE = 16;

150 

151         /**

152          * Placeholder for deleted entries.

153          */

154         private static final Object TOMBSTONE = new Object();

155 

156         /**

157          * Map entries. Contains alternating keys (ThreadLocal) and values.

158          * The length is always a power of 2.

159          */

160         private Object[] table;

161 

162         /** Used to turn hashes into indices. */

163         private int mask;

164 

165         /** Number of live entries. */

166         private int size;

167 

168         /** Number of tombstones. */

169         private int tombstones;

170 

171         /** Maximum number of live entries and tombstones. */

172         private int maximumLoad;

173 

174         /** Points to the next cell to clean up. */

175         private int clean;

176 

177         /**

178          * Constructs a new, empty instance.

179          */

180         Values() {

181             initializeTable(INITIAL_SIZE);

182             this.size = 0;

183             this.tombstones = 0;

184         }

185 

186         /**

187          * Used for InheritableThreadLocals.

188          */

189         Values(Values fromParent) {

190             this.table = fromParent.table.clone();

191             this.mask = fromParent.mask;

192             this.size = fromParent.size;

193             this.tombstones = fromParent.tombstones;

194             this.maximumLoad = fromParent.maximumLoad;

195             this.clean = fromParent.clean;

196             inheritValues(fromParent);

197         }

198 

199         /**

200          * Inherits values from a parent thread.

201          */

202         @SuppressWarnings({"unchecked"})

203         private void inheritValues(Values fromParent) {

204             // Transfer values from parent to child thread.

205             Object[] table = this.table;

206             for (int i = table.length - 2; i >= 0; i -= 2) {

207                 Object k = table[i];

208 

209                 if (k == null || k == TOMBSTONE) {

210                     // Skip this entry.

211                     continue;

212                 }

213 

214                 // The table can only contain null, tombstones and references.

215                 Reference<InheritableThreadLocal<?>> reference

216                         = (Reference<InheritableThreadLocal<?>>) k;

217                 // Raw type enables us to pass in an Object below.

218                 InheritableThreadLocal key = reference.get();

219                 if (key != null) {

220                     // Replace value with filtered value.

221                     // We should just let exceptions bubble out and tank

222                     // the thread creation

223                     table[i + 1] = key.childValue(fromParent.table[i + 1]);

224                 } else {

225                     // The key was reclaimed.

226                     table[i] = TOMBSTONE;

227                     table[i + 1] = null;

228                     fromParent.table[i] = TOMBSTONE;

229                     fromParent.table[i + 1] = null;

230 

231                     tombstones++;

232                     fromParent.tombstones++;

233 

234                     size--;

235                     fromParent.size--;

236                 }

237             }

238         }

239 

240         /**

241          * Creates a new, empty table with the given capacity.

242          */

243         private void initializeTable(int capacity) {

244             this.table = new Object[capacity * 2];

245             this.mask = table.length - 1;

246             this.clean = 0;

247             this.maximumLoad = capacity * 2 / 3; // 2/3

248         }

249 

250         /**

251          * Cleans up after garbage-collected thread locals.

252          */

253         private void cleanUp() {

254             if (rehash()) {

255                 // If we rehashed, we needn't clean up (clean up happens as

256                 // a side effect).

257                 return;

258             }

259 

260             if (size == 0) {

261                 // No live entries == nothing to clean.

262                 return;

263             }

264 

265             // Clean log(table.length) entries picking up where we left off

266             // last time.

267             int index = clean;

268             Object[] table = this.table;

269             for (int counter = table.length; counter > 0; counter >>= 1,

270                     index = next(index)) {

271                 Object k = table[index];

272 

273                 if (k == TOMBSTONE || k == null) {

274                     continue; // on to next entry

275                 }

276 

277                 // The table can only contain null, tombstones and references.

278                 @SuppressWarnings("unchecked")

279                 Reference<ThreadLocal<?>> reference

280                         = (Reference<ThreadLocal<?>>) k;

281                 if (reference.get() == null) {

282                     // This thread local was reclaimed by the garbage collector.

283                     table[index] = TOMBSTONE;

284                     table[index + 1] = null;

285                     tombstones++;

286                     size--;

287                 }

288             }

289 

290             // Point cursor to next index.

291             clean = index;

292         }

293 

294         /**

295          * Rehashes the table, expanding or contracting it as necessary.

296          * Gets rid of tombstones. Returns true if a rehash occurred.

297          * We must rehash every time we fill a null slot; we depend on the

298          * presence of null slots to end searches (otherwise, we'll infinitely

299          * loop).

300          */

301         private boolean rehash() {

302             if (tombstones + size < maximumLoad) {

303                 return false;

304             }

305 

306             int capacity = table.length >> 1;

307 

308             // Default to the same capacity. This will create a table of the

309             // same size and move over the live entries, analogous to a

310             // garbage collection. This should only happen if you churn a

311             // bunch of thread local garbage (removing and reinserting

312             // the same thread locals over and over will overwrite tombstones

313             // and not fill up the table).

314             int newCapacity = capacity;

315 

316             if (size > (capacity >> 1)) {

317                 // More than 1/2 filled w/ live entries.

318                 // Double size.

319                 newCapacity = capacity * 2;

320             }

321 

322             Object[] oldTable = this.table;

323 

324             // Allocate new table.

325             initializeTable(newCapacity);

326 

327             // We won't have any tombstones after this.

328             this.tombstones = 0;

329 

330             // If we have no live entries, we can quit here.

331             if (size == 0) {

332                 return true;

333             }

334 

335             // Move over entries.

336             for (int i = oldTable.length - 2; i >= 0; i -= 2) {

337                 Object k = oldTable[i];

338                 if (k == null || k == TOMBSTONE) {

339                     // Skip this entry.

340                     continue;

341                 }

342 

343                 // The table can only contain null, tombstones and references.

344                 @SuppressWarnings("unchecked")

345                 Reference<ThreadLocal<?>> reference

346                         = (Reference<ThreadLocal<?>>) k;

347                 ThreadLocal<?> key = reference.get();

348                 if (key != null) {

349                     // Entry is still live. Move it over.

350                     add(key, oldTable[i + 1]);

351                 } else {

352                     // The key was reclaimed.

353                     size--;

354                 }

355             }

356 

357             return true;

358         }

359 

360         /**

361          * Adds an entry during rehashing. Compared to put(), this method

362          * doesn't have to clean up, check for existing entries, account for

363          * tombstones, etc.

364          */

365         void add(ThreadLocal<?> key, Object value) {

366             for (int index = key.hash & mask;; index = next(index)) {

367                 Object k = table[index];

368                 if (k == null) {

369                     table[index] = key.reference;

370                     table[index + 1] = value;

371                     return;

372                 }

373             }

374         }

375 

376         /**

377          * Sets entry for given ThreadLocal to given value, creating an

378          * entry if necessary.

379          */

380         void put(ThreadLocal<?> key, Object value) {

381             cleanUp();

382 

383             // Keep track of first tombstone. That's where we want to go back

384             // and add an entry if necessary.

385             int firstTombstone = -1;

386 

387             for (int index = key.hash & mask;; index = next(index)) {

388                 Object k = table[index];

389 

390                 if (k == key.reference) {

391                     // Replace existing entry.

392                     table[index + 1] = value;

393                     return;

394                 }

395 

396                 if (k == null) {

397                     if (firstTombstone == -1) {

398                         // Fill in null slot.

399                         table[index] = key.reference;

400                         table[index + 1] = value;

401                         size++;

402                         return;

403                     }

404 

405                     // Go back and replace first tombstone.

406                     table[firstTombstone] = key.reference;

407                     table[firstTombstone + 1] = value;

408                     tombstones--;

409                     size++;

410                     return;

411                 }

412 

413                 // Remember first tombstone.

414                 if (firstTombstone == -1 && k == TOMBSTONE) {

415                     firstTombstone = index;

416                 }

417             }

418         }

419 

420         /**

421          * Gets value for given ThreadLocal after not finding it in the first

422          * slot.

423          */

424         Object getAfterMiss(ThreadLocal<?> key) {

425             Object[] table = this.table;

426             int index = key.hash & mask;

427 

428             // If the first slot is empty, the search is over.

429             if (table[index] == null) {

430                 Object value = key.initialValue();

431 

432                 // If the table is still the same and the slot is still empty...

433                 if (this.table == table && table[index] == null) {

434                     table[index] = key.reference;

435                     table[index + 1] = value;

436                     size++;

437 

438                     cleanUp();

439                     return value;

440                 }

441 

442                 // The table changed during initialValue().

443                 put(key, value);

444                 return value;

445             }

446 

447             // Keep track of first tombstone. That's where we want to go back

448             // and add an entry if necessary.

449             int firstTombstone = -1;

450 

451             // Continue search.

452             for (index = next(index);; index = next(index)) {

453                 Object reference = table[index];

454                 if (reference == key.reference) {

455                     return table[index + 1];

456                 }

457 

458                 // If no entry was found...

459                 if (reference == null) {

460                     Object value = key.initialValue();

461 

462                     // If the table is still the same...

463                     if (this.table == table) {

464                         // If we passed a tombstone and that slot still

465                         // contains a tombstone...

466                         if (firstTombstone > -1

467                                 && table[firstTombstone] == TOMBSTONE) {

468                             table[firstTombstone] = key.reference;

469                             table[firstTombstone + 1] = value;

470                             tombstones--;

471                             size++;

472 

473                             // No need to clean up here. We aren't filling

474                             // in a null slot.

475                             return value;

476                         }

477 

478                         // If this slot is still empty...

479                         if (table[index] == null) {

480                             table[index] = key.reference;

481                             table[index + 1] = value;

482                             size++;

483 

484                             cleanUp();

485                             return value;

486                         }

487                     }

488 

489                     // The table changed during initialValue().

490                     put(key, value);

491                     return value;

492                 }

493 

494                 if (firstTombstone == -1 && reference == TOMBSTONE) {

495                     // Keep track of this tombstone so we can overwrite it.

496                     firstTombstone = index;

497                 }

498             }

499         }

500 

501         /**

502          * Removes entry for the given ThreadLocal.

503          */

504         void remove(ThreadLocal<?> key) {

505             cleanUp();

506 

507             for (int index = key.hash & mask;; index = next(index)) {

508                 Object reference = table[index];

509 

510                 if (reference == key.reference) {

511                     // Success!

512                     table[index] = TOMBSTONE;

513                     table[index + 1] = null;

514                     tombstones++;

515                     size--;

516                     return;

517                 }

518 

519                 if (reference == null) {

520                     // No entry found.

521                     return;

522                 }

523             }

524         }

525 

526         /**

527          * Gets the next index. If we're at the end of the table, we wrap back

528          * around to 0.

529          */

530         private int next(int index) {

531             return (index + 2) & mask;

532         }

533     }

534 }
View Code

 简单的看一下Java源码,我们就会发现,你set一个对象到ThreadLocal的时候,其实这个对象是被保存到当前线程的ThreadLocalMap里的,而每一个Thread里都包含一个ThreadLocal.ThreadLocalMap threadLocals = null;代码:

1     public void set(T value) {

2         Thread t = Thread.currentThread();

3         ThreadLocalMap map = getMap(t);

4         if (map != null)

5             map.set(this, value);

6         else

7             createMap(t, value);

8     }
1     ThreadLocalMap getMap(Thread t) {

2         return t.threadLocals;

3     }
 1 public

 2 class Thread implements Runnable {

 3     /* Make sure registerNatives is the first thing <clinit> does. */

 4     private static native void registerNatives();

 5     static {

 6         registerNatives();

 7     }

 8 

 9     private char    name[];

10     private int         priority;

11     private Thread    threadQ;

12     private long    eetop;

13     ...

14     /* ThreadLocal values pertaining to this thread. This map is maintained

15      * by the ThreadLocal class. */

16     ThreadLocal.ThreadLocalMap threadLocals = null;

重点看这句:

1 map.set(this, value);

可能有人要问了,如果只是简单的将value放到Map对象里面,那不可能起到每个线程各自保存一份对象的目的啊,我们继续看map.set(this, value)方法:

 1         /**

 2          * Set the value associated with key.

 3          *

 4          * @param key the thread local object

 5          * @param value the value to be set

 6          */

 7         private void set(ThreadLocal key, Object value) {

 8 

 9             // We don't use a fast path as with get() because it is at

10             // least as common to use set() to create new entries as

11             // it is to replace existing ones, in which case, a fast

12             // path would fail more often than not.

13 

14             Entry[] tab = table;

15             int len = tab.length;

16             int i = key.threadLocalHashCode & (len-1);

17 

18             for (Entry e = tab[i];

19          e != null;

20          e = tab[i = nextIndex(i, len)]) {

21                 ThreadLocal k = e.get();

22 

23                 if (k == key) {

24                     e.value = value;

25                     return;

26                 }

27 

28                 if (k == null) {

29                     replaceStaleEntry(key, value, i);

30                     return;

31                 }

32             }

33 

34             tab[i] = new Entry(key, value);

35             int sz = ++size;

36             if (!cleanSomeSlots(i, sz) && sz >= threshold)

37                 rehash();

38         }

看到了吧,不是简单的值传递,而是重新创建了一个对象并保存起来。然后get的时候当然就是线程自己创建的对象喽,其实实现原理很简单,不是吗?

你可能感兴趣的:(threadLocal)