ThreadLocal是一个线程独立存储类,通过他的getset方法,在不同的线程中,可以独立地存取不同的value

每次回顾Looper源码的时候,都会忘了ThreadLocal是如何实现和工作的,故这次记录下来。

工作原理图

ThreadLocal - 图1

ThreadLocal中的ThreadLocalMap

ThreadLocal - 图2

首先观察ThreadLocal的结构,有两个静态内部类,其中第二个ThreadLocalMap是他工作原理的最重要的类。

此时点开Thread类源码:

  1. /* ThreadLocal values pertaining to this thread. This map is maintained
  2. * by the ThreadLocal class. */
  3. ThreadLocal.ThreadLocalMap threadLocals = null;

会发现Thread引用了一个ThreadLocal.ThreadLocalMap,并且命名为threadLocals。并且这个引用在每一个Thread对象中只有一个。这样的设计使得每个Thread对象都有一个自己的Map

Entry extends WeakRefernce

再回到ThreadLocalMap中。ThreadLocal - 图3

如果看过HashMap或者LinkedHashMap源码应该会对这个Entry的设计很熟悉。代码为:

  1. static class Entry extends WeakReference<ThreadLocal<?>> {
  2. /** The value associated with this ThreadLocal. */
  3. Object value;
  4. Entry(ThreadLocal<?> k, Object v) {
  5. super(k);
  6. value = v;
  7. }
  8. //...
  9. private Entry[] table;//Entry数组
  10. //set方法
  11. private void set(ThreadLocal<?> key, Object value) {
  12. Entry[] tab = table;
  13. int len = tab.length;
  14. int i = key.threadLocalHashCode & (len-1);
  15. for (Entry e = tab[i];
  16. e != null;
  17. e = tab[i = nextIndex(i, len)]) {
  18. ThreadLocal<?> k = e.get();
  19. if (k == key) {
  20. e.value = value;
  21. return;
  22. }
  23. if (k == null) {
  24. replaceStaleEntry(key, value, i);
  25. return;
  26. }
  27. }
  28. tab[i] = new Entry(key, value);
  29. int sz = ++size;
  30. if (!cleanSomeSlots(i, sz) && sz >= threshold)
  31. rehash();
  32. }
  33. //get方法
  34. private Entry getEntry(ThreadLocal<?> key) {
  35. int i = key.threadLocalHashCode & (table.length - 1);
  36. Entry e = table[i];
  37. if (e != null && e.get() == key)
  38. return e;
  39. else
  40. return getEntryAfterMiss(key, i, e);
  41. }
  42. }

HashMap不同的是,

  1. 他继承了WeakReferncekey必须是ThreadLocalvalue是Object,即任意类型,并且在构造函数中将key传入作为弱引用构造函数的参数。
  2. Entry并没有引用一个next之类的指针,因此无法构成HashMap那样的链表。
  3. 纯粹的是维护一个Entry数组。

继承WeakReferenc是因为:ThreadLocal作为key,如果是强引用,会一直被这个ThreadLocalMap引用,而只要后者对应的线程没有结束,那么就算在别处因为不需要了,而将该ThreadLoacal释放(=null),由于在map中的强引用,ThreadLocal对象也无法回收。

因此这里只要设计成弱引用,在别处释放后,垃圾收集器就可以顺利回收ThreadLocal对象。

ThreadLocal

ThreadLocal对象实质上是做为一个key,在不同的线程中的map里,存取不同的对象。

  1. public ThreadLocal() {
  2. //...
  3. public T get() {
  4. Thread t = Thread.currentThread();//获取当前线程
  5. ThreadLocalMap map = getMap(t);
  6. if (map != null) {
  7. ThreadLocalMap.Entry e = map.getEntry(this);//当前对象作为key去getEntry(this)
  8. if (e != null) {
  9. @SuppressWarnings("unchecked")
  10. T result = (T)e.value;
  11. return result;
  12. }
  13. }
  14. return setInitialValue();
  15. }
  16. public void set(T value) {
  17. Thread t = Thread.currentThread();//获取当前线程
  18. ThreadLocalMap map = getMap(t);
  19. if (map != null)
  20. map.set(this, value);//当前对象作为key去set(this,value)
  21. else
  22. createMap(t, value);
  23. }
  24. }

剩下的就是一些实现细节了,直接到源码里面去看就行了,主要还是看看顶部的工作原理图,理解原理就行。