ThreadLocal原理与使用

ThreadLocal用于提供线程内部的局部变量,这种变量在线程间是相互独立的,通过调用ThreadLocal的set和get方法可以实现变量的存取。
基本使用:
public class ThreadLocalTest {
    private static ThreadLocal<String> threadLocal = new ThreadLocal<String>(){
        @Override
        protected String initialValue() {
            return "initValue";
        }
    };
    public static void main(String[] args) {
        for (int i=0;i<5;i++){
            int finalI = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    threadLocal.set("thread-"+finalI);
                    System.out.println(threadLocal.get());
                    threadLocal.remove();
                    System.out.println(threadLocal.get());
                }
            }).start();
        }
        System.out.println("main-" + threadLocal.get());
    }
}
输出
thread-0
thread-2
thread-3
main-initValue
thread-4
thread-1
在以上代码中,我们通过重写ThreadLocal的initialValue()方法设定其初始值,在主线程中,由于没有调用set()方法赋值,调用get()方法得到的值就是initialValue()方法的返回值。在循环体中的子线程中,通过调用ThreadLocal的set()方法为每个线程赋值,那么get()方法得到的就是为该线程设置的值,在各个线程之间相互独立。
ThreadLocal的set()方法如下:
public void set(T value) {
   //首先得到当前线程
   Thread t = Thread.currentThread();
   // 获取此线程对象中维护的ThreadLocalMap对象
   ThreadLocalMap map = getMap(t);
   //如果map不为空,则设置值
   if (map != null)
      map.set(this, value);
   else
      //如果map为空,则创建ThreadLocalMap对象并将value存至map
      createMap(t, value);
}
ThreadLocal的get方法如下:
public T get() {
     //首先得到当前线程
     Thread t = Thread.currentThread();
     // 获取此线程对象中维护的ThreadLocalMap对象
     ThreadLocalMap map = getMap(t);
     // 如果此map存在,以当前的ThreadLocal对象为key,调用map的getEntry获取Entry对象
     if (map != null) {
         ThreadLocalMap.Entry e = map.getEntry(this);
         if (e != null) {
             // 如果entry对象不为空,获取entry对象的 value值
             T result = (T)e.value;
             return result;
          }
      }
            // 如果map不存在,调用setInitialValue进行初始化
      return setInitialValue();
}
ThreadLocald的setInitialValue()方法如下:
private T setInitialValue() {
    //首先获取初始值,initialValue()方法默认为null,用户可重写该方法
        T value = initialValue();
        //得到当前线程
        Thread t = Thread.currentThread();
        // 获取此线程对象中维护的ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        if (map != null)
        //如果map不为空,则设置初始值
            map.set(this, value);
        else
        //如果map为空,则创建ThreadLocalMap对象并将value存至map
            createMap(t, value);
        return value;
}
ThreadLocal底层使用Thread对象中的ThreadLocalMap进行数据存储,当在ThreadLocal中调用createMap(t,value)时,会调用ThreadLocalMap的构造方法,如下:
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    //初始化一个Entry数组,默认大小为16
    table = new Entry[INITIAL_CAPACITY];
    //计算value在数组中的位置
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    //创建一个Entry对象,key为ThreadLocal对象,值是value,并保存在数组中
    table[i] = new Entry(firstKey, firstValue);
    //设置数组当前原素数量
    size = 1;
    //设置扩容阈值,为table长度 * 2/3;
    setThreshold(INITIAL_CAPACITY);
}
ThreadLocal的set方法最终调用了ThreadLocalMap的set(ThreadLocal<?> key, Object value)方法,该方法如下:
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();
}

在以上代码中,首先根据key(ThreadLocal对象)值计算该元素的存储位置,然后遍历table数组,当entry不为空,将key与数组元素entry的threadLocal对象进行比较,如果key值与threadLocal对应,则将新值赋给找到的entry对象的value上。如果entry的key为空,则调用replaceStaleEntry方法把key为空的entry替换为当前要设置的entry。如果遍历数组时遇到entry为空,则创建新的entry对象并将其放在entry为空的位置,并且数组元素个数加1,最后调用cleanSomeSlots清除过期元素并判断数组元素个数有没有达到阈值,如果达到阈值,则进行扩容。
ThreadLocal的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);
}
在以上代码中,首先计算key对应的entry对象的存储位置,然后根据位置在数组中拿到entry对象,如果entry对象不为null并且entry的key与方法中传入的key相同,则返回entry对象,否则执行getEntryAfterMiss(key, i, e)方法,在此方法中,对数组中元素进行遍历,如果entry对象的key不为null,则与传入的key进行比较,相同则返回此entry对象;如果entry对象的key为null,说明该对象已过期,则调用expungeStaleEntry方法清除过期对象。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值