ThreadLocal源码分析

ThreadLocal静态内部类

ThreadLocalMap
ThreadLocalMap成员变量
// Entry数组初始化容量
private static final int INITIAL_CAPACITY = 16;
// Entry数组,用来存储ThreadLocalMap中的数据
private Entry[] table;
// Entry数组的实际元素个数
private int size = 0;
// 阈值,初始值为0,
// 在ThreadLocalMap创建时默认大小为16 * 2/3 = 10
// 在扩容的时候默认大小为old * 2 * 2/3
private int threshold;
ThreadLocalMap构造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue)
/**
 * ThreadLocalMap对象构造方法,为Entry数组默认开辟16个空间
 */
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
  // INITIAL_CAPACITY默认为16,table数组创建化大小也为16
  table = new Entry[INITIAL_CAPACITY];
  
  // 获取key(即当前ThreadLocal对象)的散列值
  int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
  
  // 创建Entry对象,并添加到Entry数组中指定位置
  table[i] = new Entry(firstKey, firstValue);
  
  size = 1;
  
  // 设置阈值
  setThreshold(INITIAL_CAPACITY);
}
ThreadLocalMap(ThreadLocalMap parentMap)
/**
 * 在Thread初始化时调用此方法进行创建ThreadLocalMap方法进行初始化ThreadLocalMap
 */
private ThreadLocalMap(ThreadLocalMap parentMap) {
  Entry[] parentTable = parentMap.table;
  int len = parentTable.length;
  setThreshold(len);
  table = new Entry[len];

  for (int j = 0; j < len; j++) {
    Entry e = parentTable[j];
    if (e != null) {
      @SuppressWarnings("unchecked")
      ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
      if (key != null) {
        Object value = key.childValue(e.value);
        Entry c = new Entry(key, value);
        int h = key.threadLocalHashCode & (len - 1);
        while (table[h] != null)
          h = nextIndex(h, len);
        table[h] = c;
        size++;
      }
    }
  }
}
ThreadLocalMap静态内部类
Entry静态内部类
/**
 * 静态内部类ThreadLocalMap中的静态内部类Entry,主要用于存储ThreadLocal中的线程副本变量,key为ThreadLocal对象,value为副本变量值
 */
static class Entry extends WeakReference<ThreadLocal<?>> {
  Object value;

  Entry(ThreadLocal<?> k, Object v) {
    super(k);
    value = v;
  }
}
ThreadLocalMap主要成员方法
getEntry(ThreadLocal<?> key)
/**
 * 从ThreadLocal静态内部类ThreadLocalMap对象的Entry数组中根据key(当前ThreadLocal对象)获取Entry对象
 */
private Entry getEntry(ThreadLocal<?> key) {
  // 获取key(即当前ThreadLocal对象)的散列值
  int i = key.threadLocalHashCode & (table.length - 1);
  
  // 当Entry数组中存在该key
  Entry e = table[i];
  if (e != null && e.get() == key)
    return e;
  
  // 当Entry数组中不存在该key,
  else
    return getEntryAfterMiss(key, i, e);
}
getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e)
/**
 * 从ThreadLocal静态内部类ThreadLocalMap对象的Entry数组中获取Entry对象时Entry不存在该key时获取Entry对象
 */
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;
}
set(ThreadLocal<?> key, Object value)
/**
 * 为当前线程在ThreadLocal对象中设置值实现
 */
private void set(ThreadLocal<?> key, Object value) {
  Entry[] tab = table;
  int len = tab.length;

  // 获取key(即当前ThreadLocal对象)的散列值
  int i = key.threadLocalHashCode & (len-1);

  for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
    // 从Entry数组中获取ThreadLocalMap的key
    ThreadLocal<?> k = e.get();

    // 当ThreadLocalMap中的Entry数组中存在该key,直接替换value值
    if (k == key) {
      e.value = value;
      return;
    }

    // 当ThreadLocalMap中的Entry数组中不存在该key,Entry中的ThreadLocal对象是弱引用类型,当ThreadLocal被回收了,当前key引用就为空引用,就需要将当前key引用从Entry数组中进行替换
    if (k == null) {
      replaceStaleEntry(key, value, i);
      return;
    }
  }

  // 创建Entry对象,并添加到Entry数组中指定位置
  tab[i] = new Entry(key, value);
  
  int sz = ++size;
  
  // 判断是否需要扩容
  if (!cleanSomeSlots(i, sz) && sz >= threshold)
    rehash();
}
remove(ThreadLocal<?> key)
/**
 * 为当前线程在ThreadLocal对象中删除副本变量实现
 */
private void remove(ThreadLocal<?> key) {
  Entry[] tab = table;
  int len = tab.length;
  
  // 获取key(即当前ThreadLocal对象)的散列值
  int i = key.threadLocalHashCode & (len-1);
  
  for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
    // 当Entry数组中存在当前ThreadLocal对象为key的Entry
    if (e.get() == key) {
      // 删除Entry数组中对当前ThreadLocal对象的引用
      e.clear();
      
      // 整理Entry数组
      expungeStaleEntry(i);
      
      return;
    }
  }
}
nextIndex(int i, int len)
/**
 * 索引下标在不越界的情况下+1
 */
private static int nextIndex(int i, int len) {
  return ((i + 1 < len) ? i + 1 : 0);
}
prevIndex(int i, int len)
/**
 * 索引下标在不越界的情况下-1
 */
private static int prevIndex(int i, int len) {
  return ((i - 1 >= 0) ? i - 1 : len - 1);
}
rehash()
/**
 * 判断是否需要扩容
 */
private void rehash() {
  // 清除脏的Entry实体对象
  expungeStaleEntries();

  // 如果在当前size >= 阈值*0.75就进行扩容
  if (size >= threshold - threshold / 4)
    resize();
}
resize()
/**
 * 对table数组进行扩容
 */
private void resize() {
  Entry[] oldTab = table;
  
  // 新的table数组为之前的2倍
  int oldLen = oldTab.length;
  int newLen = oldLen * 2;
  Entry[] newTab = new Entry[newLen];
  
  int count = 0;

  // 循环遍历旧table数组,在添加到新table数组时判断key非null
  for (int j = 0; j < oldLen; ++j) {
    Entry e = oldTab[j];
    if (e != null) {
      ThreadLocal<?> k = e.get();
      if (k == null) {
        // 如果key为null时,value也设置为null,让下一次GC来临之时把对象回收掉
        e.value = null;
      } else {
        int h = k.threadLocalHashCode & (newLen - 1);
        while (newTab[h] != null)
          h = nextIndex(h, newLen);
        newTab[h] = e;
        count++;
      }
    }
  }

  // 设置阈值
  setThreshold(newLen);
  
  size = count;
  table = newTab;
}
setThreshold(int len)
/**
 * 设置阈值
 */
private void setThreshold(int len) {
  threshold = len * 2 / 3;
}
expungeStaleEntry(int staleSlot)
/**
 * 清除脏的Entry实体对象
 * 
 * 就是从staleSlot开始遍历,将无效(弱引用指向对象被回收)清理,即对应entry中的value置为null,将指向这个entry的table[i]置为null,直到扫到空entry。
 * 另外,在过程中还会对非空的entry做rehash操作
 */
private int expungeStaleEntry(int staleSlot) {
  Entry[] tab = table;
  int len = tab.length;

  // 因为在调用此方法之前,Entry对象的key就已经为null了,所以现在将当前staleSlot索引下的Entry对象置为null,提前让对象在下一次GC的时候进行对象回收
  tab[staleSlot].value = null;
  tab[staleSlot] = null;
  size--;

  // 执行rehash(),直到遇到了table[i]=null
  Entry e;
  int i;
  
  // for循环从staleSlot开始到len-1,直到table[i]==null终止
  for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {
    ThreadLocal<?> k = e.get();
    
    // 如果在循环的过程中遇到了脏的Entry对象,即Entry对象的key为null,那么同样将这个Entry对象也设置为null,让GC进行回收
    if (k == null) {
      e.value = null;
      tab[i] = null;
      size--;
    }
    
    // 对于table数组中还没有回收的Entry对象,需要做一次rehash(即重新整理Entry数组中的元素)
    else {
      // 根据hashcode计算k在不冲突情况下散列表的索引
      int h = k.threadLocalHashCode & (len - 1);
      
      // h != i说明i位置上发生了hash冲突,导致ThreadLocal的索引值从i向后移动到了h。
      // 此时,查找h位置往后第一个为null的slot(槽),并将原先在i索引处位置的Entry对象移动到该位置。
      if (h != i) {
        tab[i] = null;

        // 如果table[h]位置存在元素,则h向后移动一位
        while (tab[h] != null)
          h = nextIndex(h, len);
        
        // 将还没有回收的Entry对象移动到Entry数组中的h索引下
        tab[h] = e;
      }
    }
  }
  return i;
}
replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot)
/**
 * 替换脏的Entry实体对象
 * 
 * 当ThreadLocalMap中的Entry数组中不存在该key,Entry中的ThreadLocal对象是弱引用类型,当ThreadLocal被回收了,当前key引用就为空引用,就需要将当前key引用从Entry数组中进行替换
 */
private void replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot) {
  Entry[] tab = table;
  int len = tab.length;
  Entry e;

  // 以staleSlot索引-1处开始往前遍历寻找第一个为null的solt(槽)
  int slotToExpunge = staleSlot;
  for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len))
    if (e.get() == null)
      slotToExpunge = i;

  // 以staleSlot索引+1处开始往后遍历
  for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {
    ThreadLocal<?> k = e.get();

    // 当循环遍历中的key与入参的key一致时,对两个slot(槽)中的Entry对象进行交换
    if (k == key) {
      e.value = value;

      tab[i] = tab[staleSlot];
      tab[staleSlot] = e;

      if (slotToExpunge == staleSlot)
        slotToExpunge = i;
      
      // 做一次脏Entry数组对象清理
      cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
      
      return;
    }
    
    if (k == null && slotToExpunge == staleSlot)
      slotToExpunge = i;
  }

  // 如果在Entry数组中没有找到相同的key的Entry元素对象进行替换,就新建一个Entry对象放在staleSlot索引处
  tab[staleSlot].value = null;
  tab[staleSlot] = new Entry(key, value);

  if (slotToExpunge != staleSlot)
    cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
}
cleanSomeSlots(int i, int n)
/**
 * 以对数的形式遍历寻找Entry数组中脏的Entry元素,然后调用expungeStaleEntry(int staleSlot)进行清理脏的Entry元素
 */
private boolean cleanSomeSlots(int i, int n) {
  boolean removed = false;
  Entry[] tab = table;
  int len = tab.length;
  
  // 以对数的形式遍历Entry数组,寻找Entry数组中Entry对象存在,但是对象中的key不存在的Entry对象,调用expungeStaleEntry(int staleSlot)方法进行清理
  do {
    i = nextIndex(i, len);
    Entry e = tab[i];
    if (e != null && e.get() == null) {
      n = len;
      removed = true;
      
      // 清除脏的Entry实体对象
      i = expungeStaleEntry(i);
    }
  } while ( (n >>>= 1) != 0);
  
  return removed;
}

ThreadLocal主要成员方法

get()
/**
 * 从ThreadLocal对象中获取当前线程的副本变量
 */
public T get() {
  // 获取当前线程
  Thread t = Thread.currentThread();

  // 获取当前线程的ThreadLocal静态内部类ThreadLocalMap对象
  ThreadLocalMap map = getMap(t);

  // 当前线程存在副本
  if (map != null) {
    // 从当前线程ThreadLocalMap对象中获取Entry对象
    ThreadLocalMap.Entry e = map.getEntry(this);

    // 从Entry对象中获取value值(线程副本变量)
    if (e != null) {
      T result = (T)e.value;
      return result;
    }
  }

  // 当前线程不存在副本
  return setInitialValue();
}

/**
 * 从当前线程中获取ThreadLocal.ThreadLocalMap对象
 */
ThreadLocalMap getMap(Thread t) {
  return t.threadLocals;
}

/**
 * 从ThreadLocal静态内部类ThreadLocalMap对象的Entry数组中根据key(当前ThreadLocal对象)获取Entry对象
 */
private Entry getEntry(ThreadLocal<?> key) {
  // 获取key(即当前ThreadLocal对象)的散列值
  int i = key.threadLocalHashCode & (table.length - 1);
  
  // 当Entry数组中存在该key
  Entry e = table[i];
  if (e != null && e.get() == key)
    return e;
  
  // 当Entry数组中不存在该key,
  else
    return getEntryAfterMiss(key, i, e);
}

/**
 * 从ThreadLocal静态内部类ThreadLocalMap对象的Entry数组中获取Entry对象时Entry不存在该key时获取Entry对象
 */
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;
}

/**
 * 为当前线程在ThreadLocal对象中不存在时设置初始值
 */
private T setInitialValue() {
  // 获取初始值
  T value = initialValue();

  // 获取当前线程
  Thread t = Thread.currentThread();

  // 获取当前线程的ThreadLocal静态内部类ThreadLocalMap对象
  ThreadLocalMap map = getMap(t);

  // 当前线程存在副本,直接替换原来的值
  if (map != null)
    map.set(this, value);

  // 当前线程不存在副本,使用副本初始值来当前线程设置初始值
  else
    createMap(t, value);
  return value;
}

/**
 * 为当前线程在ThreadLocal对象中不存在时的初始值,默认为null
 */
protected T initialValue() {
  return null;
}
set(T value)
/**
 * 为当前线程在ThreadLocal对象中设置副本变量
 */
public void set(T value) {
  Thread t = Thread.currentThread();
  ThreadLocalMap map = getMap(t);
  if (map != null)
    map.set(this, value);
  else
    createMap(t, value);
}

/**
 * 从当前线程中的ThreadLocal.ThreadLocalMap对象中获取ThreadLocalMap对象值
 */
ThreadLocalMap getMap(Thread t) {
  return t.threadLocals;
}

/**
 * 为当前线程在ThreadLocal对象中设置值实现
 */
private void set(ThreadLocal<?> key, Object value) {
  Entry[] tab = table;
  int len = tab.length;
  
  // 获取key(即当前ThreadLocal对象)的散列值
  int i = key.threadLocalHashCode & (len-1);

  for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
    // 从Entry数组中获取ThreadLocalMap的key
    ThreadLocal<?> k = e.get();

    // 当ThreadLocalMap中的Entry数组中存在该key,直接替换value值
    if (k == key) {
      e.value = value;
      return;
    }

    // 当ThreadLocalMap中的Entry数组中不存在该key,Entry是弱引用类型,当ThreadLocal被回收了,当前key引用为空引用,就需要将当前key引用从Entry数组中移除
    if (k == null) {
      replaceStaleEntry(key, value, i);
      return;
    }
  }

  // 创建Entry对象,并添加到Entry数组中指定位置
  tab[i] = new Entry(key, value);
  
  int sz = ++size;
  
  // 判断是否需要扩容
  if (!cleanSomeSlots(i, sz) && sz >= threshold)
    rehash();
}

/**
 * 为当前线程中的ThreadLocalMap对象创建
 */
void createMap(Thread t, T firstValue) {
  t.threadLocals = new ThreadLocalMap(this, firstValue);
}

/**
 * 初始化ThreadLocalMap对象
 */
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
  // INITIAL_CAPACITY默认为16,table数组创建化大小也为16
  table = new Entry[INITIAL_CAPACITY];
  
  // 获取key(即当前ThreadLocal对象)的散列值
  int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
  
  // 创建Entry对象,并添加到Entry数组中指定位置
  table[i] = new Entry(firstKey, firstValue);
  
  size = 1;
  
  // 设置阈值
  setThreshold(INITIAL_CAPACITY);
}

/**
 * 设置阈值
 */
private void setThreshold(int len) {
  threshold = len * 2 / 3;
}
remove()
/**
 * 为当前线程在ThreadLocal对象中删除副本变量
 */
public void remove() {
  // 从当前线程中的ThreadLocal.ThreadLocalMap对象中获取ThreadLocalMap对象值
  ThreadLocalMap m = getMap(Thread.currentThread());
  
  // 当ThreadLocalMap对象存在时
  if (m != null)
    m.remove(this);
}

/**
 * 从当前线程中的ThreadLocal.ThreadLocalMap对象中获取ThreadLocalMap对象值
 */
ThreadLocalMap getMap(Thread t) {
  return t.threadLocals;
}

/**
 * 为当前线程在ThreadLocal对象中删除副本变量实现
 */
private void remove(ThreadLocal<?> key) {
  Entry[] tab = table;
  int len = tab.length;
  
  // 获取key(即当前ThreadLocal对象)的散列值
  int i = key.threadLocalHashCode & (len-1);
  
  for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
    // 当Entry数组中存在当前ThreadLocal对象为key的Entry
    if (e.get() == key) {
      // 删除Entry数组中对当前ThreadLocal对象的引用
      e.clear();
      
      // 整理Entry数组
      expungeStaleEntry(i);
      
      return;
    }
  }
}

/**
 * 删除Entry数组中对当前ThreadLocal对象的引用
 */
public void clear() {
  this.referent = null;
}
initialValue()
/**
 * 为当前ThreadLocal对象设置初始值,可以重写该方法,自定义初始值
 */
protected T initialValue() {
  return null;
}

ThreadLocal总结

  • ThreadLocal为共享变量在每个线程中创建一个副本,每个线程访问自己内部的副本变量
  • ThreadLocal的实现主要在ThreadLocal的静态内部类ThreadLocalMap中进行实现的;在Thread类中定义了ThreadLocal.ThreadLocalMap对象,每个线程独有的ThreadLocalMap来存储本线程ThreadLocal中定义的变量,都存储在ThreadLocalMap中的Entry数组中
  • ThreadLocalMap的静态内部类Entry继承了WeakReference,Entry中的key为ThreadLocal对象,它是弱引用类型的,value为存储在ThreadLocal中的变量副本
  • 在线程中使用完ThreadLocal对象中的数据,应该调用remove()方法进行清除数据,防止内存泄漏
  • ThreadLocalMap中的expungeStaleEntry(int staleSlot)方法并不能清理所有脏的Entry对象(即Entry对象存在,但是Entry的key为null),只有扩容resize()方法才能遍历整个Entry数组进行清除
ThreadLocal源码Java中一个关键的类,它提供了一种在多线程环境下实现线程本地变量的机制。在JDK 8之前和之后,ThreadLocal的内部结构有所变化。ThreadLocal源码分为两部分:ThreadLocal类和ThreadLocalMap类。 ThreadLocal类是一个泛型类,它包含了两个核心方法:set()和get()。set()方法用于将一个值与当前线程关联起来,get()方法用于获取当前线程关联的值。 ThreadLocalMap类是ThreadLocal的内部类,它用于存储每个线程的本地变量。在JDK 8之前,ThreadLocalMap是通过线性探测法解决哈希冲突的,每个ThreadLocal对象都对应一个Entry对象,Entry对象包含了ThreadLocal对象和与之关联的值[2]。 在JDK 8之后,ThreadLocalMap的实现方式发生了改变。使用了类似于HashMap的方式,采用了分段锁的机制来提高并发性能。每个线程维护一个ThreadLocalMap对象,其中的Entry对象也是采用链表的形式来解决哈希冲突。 总结起来,ThreadLocal源码主要由ThreadLocal类和ThreadLocalMap类组成。ThreadLocal类提供了set()和get()方法来管理线程本地变量,而ThreadLocalMap类则负责存储每个线程的本地变量,并解决哈希冲突的问题。 史上最全ThreadLocal 详解 ThreadLocal源码分析_02 内核(ThreadLocalMap) 【JDK源码】线程系列之ThreadLocal 深挖ThreadLocal ThreadLocal原理及内存泄露预防 ThreadLocal原理详解——终于弄明白了ThreadLocal ThreadLocal使用与原理 史上最全ThreadLocal 详解。 ThreadLocal源码分析,主要有ThreadLocal源码以及ThreadLocal的内部结构在jdk8前后的变化。 使用方式非常简单,核心就两个方法set/get public class TestThreadLocal { private static final ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { try { threadLocal.set("aaa"); Thread.sleep(500); System.out.println("threadA:" threadLocal.get()); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { threadLocal.set("bbb"); System.out.println("threadB:" threadLocal.get()); } }).start(); } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值