ThreadLocal源码阅读笔记

  JDK上对于ThreadLocal的说明是“该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。”由此可见,ThreadLocal保存的是线程自己变量,其他线程是不可见的。

  在ThreadLocal的实现中,定义了一个嵌套类ThreadLocalMap。该类也定义了一个嵌套类Entry,用以存放线程的一个局部变量。ThreadLocalMap定义了一个Entry数组,用以存放线程的多个局部变量。每个局部变量都以ThreadLocal对象为键,T类型对象为值(T为ThreadLocalMap<T>中的T类型)。而在Thread类中定义了一个ThreadLocalMap类型的对象map,这些局部变量都放在这个对象内。

  提供的方法:

 T get() 
          返回此线程局部变量的当前线程副本中的值。
protected  T initialValue() 
          返回此线程局部变量的当前线程的“初始值”。
 void remove() 
          移除此线程局部变量当前线程的值。
 void set(T value) 
          将此线程局部变量的当前线程副本中的值设置为指定值。

其中initialValue()用来设置初始值。当没有set()时就调用get()或者remove()之后没有set()就调用get()都会调用initialValue()进行初始化。当继承了ThreadLocal后可以覆盖该方法以指定不同的初值(默认为null)。

  T get()调用了ThreadLocalMap的getEntry(ThreadLocal key)方法来寻找对应的值。

  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);
  }

  //碰撞后采用线性探测法来处理,遇到老化了的Entry会调用expungeStaleEntry进行删除。

  //而该函数不仅删除该Entry,还会将在这一块碰撞区域老化了的Entry删除,并调整没有老化的Entry的位置

  //使得Entry更靠近它原本应该所在的位置,下一次探测时更快找到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;
  }

  

  void remove()调用ThreadLocalMap的remove(ThreadLocal key)进行删除

  //删除时是将该Entry的key设为null,并删除该Entry(同样是调用expungeStaleEntry进行删除)

  private void remove(ThreadLocalTest 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;
      }
    }
  }

  void set(T value)是通过调用ThreadLocalMap中的set(ThreadLocal key, Object value)完成的

  //如果map中有该键值对的Entry,则将其值设为value返回

  //如果在线性探测的过程中遇到了老化的Entry则调用replaceStaleEntry(ThreadLocal key, Object value, int staleSlot)进行取代

  //否则将包含该键值对的Entry加入到map中,如果超过了capacity(默认为table.length的2/3),需要进行重新配置map

  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 == e.get()) {
        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();
  }

  对于replaceStaleEntry(ThreadLocal key, Object value, int staleSlot),它会在当前碰撞快的staleSlot之后找是否有相应的键值对,如果有,设置相应的值,然后清理所有的老化的Entry;如果没有,则在staleSlot处放置Entry(key, value),然后也清理所有的老化的Entry。

 

posted @ 2015-05-02 21:05  deng_huan  阅读(105)  评论(0编辑  收藏  举报