ThreadLocal原理解析

47 阅读4分钟

ThreadLocal是什么

ThreadLocal是Thread Local Variable 线程局部变量,其功能就是为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量一样。

ThreadLocal类用法

ThreadLocal提供三个public方法

  • T get(): 返回此线程局部变量中当前线程副本中的值。
  • void remove(): 删除些线程局部变量中当前线程的值。
  • void set(T value): 设置此线程局部变量中当前线程副本中的值。

ThreadLocal作用

  • 适用于多线程情况下,在调用不同方法之间实现线程隔离的数据传递(而不用单独封装参数)。如在链路追踪中要记录traceId,spanId等。
  • 适用于多线程情况下,隔离多个线程的对数据的访问冲突,避免线程对变量资源的竞争。

内存泄漏问题说明与解决

内存泄漏原因:

  • 由于ThreadLocalMap中Entry数组中Entry对象的K值为弱引用,即ThreadLocal为弱引用,如果没有强引用指向它(即线程还在,但是没有用这个ThreadLocal了),它在gc中会被回收,从面key为空,但是value是强引用还在。由于key为null了,所以value无论如何访问不到了,由此存在内存泄漏问题。
  • ThreadLocalMap也尽量的避免内存泄漏,其在set、get、remove处都设置了清理脏数据逻辑。(通过弱引用功能标记脏数据,然后在后面逻辑中通过标记清理脏数据)。
    • 调用set(T)方法时,清理脏数据,扩容时也清理。
    • 调用get()方法时,没有直接命中,后面环形查找时清理脏数据。
    • 调用remove()方法时,除了清理当前Entry,还会向后清理。 解决内存泄漏问题:
  • ThreadLoalMap,记得remove掉,避免数据遗留内存泄漏。

弱引用是尽量解决内存被占用的问题,通过弱引用标记,然后在set、get、remove方法中根据key为null的标记将脏数据清理,释放内存。试想,如果不用弱引用,那ThreadLocal则无法做到自动清理,导致必须手动remove。

强软弱虚引用

  • 强引用(Strongly Reference):指代码中普遍存在的赋值行为,如:Object o = new Object(),只要强引用关系还在,对象就永远不会被回收。
  • 软引用(Soft Reference):还有用处,但是非必须存活的对象,JVM会在内存溢出前对其进行回收,例如:缓存。
  • 弱引用(Weak Reference):非必须存活的对象,引用关系比软引用还弱,不管内存是否够用,下次GC一定回收。
  • 虚引用(Phantom Reference):也称“幽灵引用”、“幻影引用”,最弱的引用关系,完全不影响对象的回收,等同于没有引用,虚引用的唯一的目的是对象被回收时会收到一个系统通知。

其它

ThreadLocal类及方法简要说明

public class Thread implements Runnable {
  ThreadLocal.ThreadLocalMap threadLocals = null;
}
public class ThreadLocal<T> {
  // 定义初始hashcode
  private static AtomicInteger nextHashCode =
        new AtomicInteger();
  private static final int HASH_INCREMENT = 0x61c88647;
  
  // 每次new新的ThreadLocal对象时hashcode自增,用于同一线程,多个ThreadLocal对象时,设置key,value
  private final int threadLocalHashCode = nextHashCode();
 	private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
  // 设置线程局部变量
  public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
    }
  // 每一个线程,对应一个ThreadLocalMap,这里新建map并增加key,value值。
  void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
  // 移除值
  public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null) {
             m.remove(this);
         }
     }
  // get线程局部变量
  public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
  // ThreadLocalMap用于保存线程局部变量。每个线程都对应一个ThreadLocalMap
  static class ThreadLocalMap {
     static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                // 设置k为弱引用,即key为弱引用
                super(k);
                // value为强引用
                value = v;
            }
        }
     ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            // 申明entry数组用于保存Entry(key,value)对象
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
     // 获取ThreadLocal对象保存值
     private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }
     // 未获取到值时,向下继续找,并随便清理脏数据(key为空,而value不为空的对象)
     private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;

            while (e != null) {
                ThreadLocal<?> k = e.get();
                if (k == key)
                    return e;
                if (k == null)
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }
     // 移除值,除了清理给定key值还要清理脏数据
     private void remove(ThreadLocal<?> key) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }
     // 设置值,并清理脏数据
     private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
  }
}