一、ThredLocal作用

每一个线程都有自己专属的本地变量副本,主要解决了让每个线程绑定自己的值,通过使用get()和set()方法,获取默认值或将其值更改为当前线程所存的副本的值从而避免了线程安全问题。

二、源码分析

2.1ThredLocal中get方法

  1. public T get() {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t);
  4. if (map != null) {
  5. ThreadLocalMap.Entry e = map.getEntry(this);
  6. if (e != null) {
  7. @SuppressWarnings("unchecked")
  8. T result = (T)e.value;
  9. return result;
  10. }
  11. }
  12. return setInitialValue();
  13. }

初始化setInitialValue()

  1. private T setInitialValue() {
  2. T value = initialValue();
  3. Thread t = Thread.currentThread();
  4. ThreadLocalMap map = getMap(t);
  5. if (map != null)
  6. map.set(this, value);
  7. else
  8. createMap(t, value);
  9. return value;
  10. }

创建map—》createMap(t, value);

  1. void createMap(Thread t, T firstValue) {
  2. t.threadLocals = new ThreadLocalMap(this, firstValue);
  3. }

ThreadMap构造方法

  1. ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
  2. table = new Entry[INITIAL_CAPACITY];
  3. int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
  4. table[i] = new Entry(firstKey, firstValue);
  5. size = 1;
  6. setThreshold(INITIAL_CAPACITY);
  7. }

2.1ThredLocal中set方法

  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. }

map.set(this,value)方法

  1. private void set(ThreadLocal<?> key, Object value) {
  2. // We don't use a fast path as with get() because it is at
  3. // least as common to use set() to create new entries as
  4. // it is to replace existing ones, in which case, a fast
  5. // path would fail more often than not.
  6. Entry[] tab = table;
  7. int len = tab.length;
  8. int i = key.threadLocalHashCode & (len-1);
  9. for (Entry e = tab[i];
  10. e != null;
  11. e = tab[i = nextIndex(i, len)]) {
  12. ThreadLocal<?> k = e.get();
  13. if (k == key) {
  14. e.value = value;
  15. return;
  16. }
  17. if (k == null) {
  18. replaceStaleEntry(key, value, i);
  19. return;
  20. }
  21. }
  22. tab[i] = new Entry(key, value);
  23. int sz = ++size;
  24. if (!cleanSomeSlots(i, sz) && sz >= threshold)
  25. rehash();
  26. }

2.1ThredLocal中remove方法

  1. public void remove() {
  2. ThreadLocalMap m = getMap(Thread.currentThread());
  3. if (m != null)
  4. m.remove(this);
  5. }

m.remove(this)方法

  1. private void remove(ThreadLocal<?> key) {
  2. Entry[] tab = table;
  3. int len = tab.length;
  4. int i = key.threadLocalHashCode & (len-1);
  5. for (Entry e = tab[i];
  6. e != null;
  7. e = tab[i = nextIndex(i, len)]) {
  8. if (e.get() == key) {
  9. e.clear();
  10. expungeStaleEntry(i);
  11. return;
  12. }
  13. }
  14. }

删除方法expungeStaleEntry(i);

  1. private int expungeStaleEntry(int staleSlot) {
  2. Entry[] tab = table;
  3. int len = tab.length;
  4. // expunge entry at staleSlot
  5. tab[staleSlot].value = null;
  6. tab[staleSlot] = null;
  7. size--;
  8. // Rehash until we encounter null
  9. Entry e;
  10. int i;
  11. for (i = nextIndex(staleSlot, len);
  12. (e = tab[i]) != null;
  13. i = nextIndex(i, len)) {
  14. ThreadLocal<?> k = e.get();
  15. if (k == null) {
  16. e.value = null;
  17. tab[i] = null;
  18. size--;
  19. } else {
  20. int h = k.threadLocalHashCode & (len - 1);
  21. if (h != i) {
  22. tab[i] = null;
  23. // Unlike Knuth 6.4 Algorithm R, we must scan until
  24. // null because multiple entries could have been stale.
  25. while (tab[h] != null)
  26. h = nextIndex(h, len);
  27. tab[h] = e;
  28. }
  29. }
  30. }
  31. return i;
  32. }

三、为什么ThreadLocal要使用弱引用?

当function1方法执行完毕后,栈帧销毁强引用tl也就没有了。但此时线程的ThreadLocalMap里某个entryi的key引用还指向这个对象若这个key引用是强引用,就会导致key指向的ThreadLocal对象及v指向的对象不能被gc回收,造成内存泄漏;
若这个key引用是弱引用就大概率会减少内存泄漏的问题(还有一个key为null的雷)。使用弱引用,就可以使ThreadLocal对象在方法执行完毕后顺利被回收且Entry的key引用指向为null。