ThreadLocal总结(jdk1.8源码)

Posted coderlynn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ThreadLocal总结(jdk1.8源码)相关的知识,希望对你有一定的参考价值。

ThreadLocal为每个使用它的线程提供一个变量的副本。
ThreadLocal中的主要方法:
 
  1. public void set(T value)
 
public void set(T value) {
        Thread t = Thread.currentThread();
         //取得线程t的ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

 

 
     先调用Thread类的静态方法获得当前线程的Thread对象,每个线程对应的Thread对象都有一个ThreadLocalMap对象的引用,如下。
    
  ThreadLocal.ThreadLocalMap threadLocals = null;     //Thread类中

 

     然后获得当前线程的ThreadLocalMap对象
ThreadLocalMap getMap(Thread t) {     //ThreadLocal类内的方法
        return t.threadLocals;
    }

 

     如果不为空就调用set方法,如果为空就调用createMap方法,传入参数为ThreadLocalMap为空的Thread对象,和T类型的firstValue
 void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

 

使用ThreadLocalMap的重载构造器,构造Thread对象的ThreadLocalMap,传入参数为当前的ThreadLocal对象,和firstValue
 private static final int INITIAL_CAPACITY = 16;          //ThreadLocalMap中定义的

 

 
 private final int threadLocalHashCode = nextHashCode();          //ThreadLocal中定义的

 

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

 

i为threadLocal对象的哈希值和Entry数组的大小-1的二进制值(1111)的操作结果,这样能保证存储在数组的每一个位置的概率相同。
创建一个Entry(键值对)数组,存放下标为threadLocal对象的哈希值和1111的与操作结果,存放对象的键为threadLocal对象,值为firstValue即set的值
 
firstValue对象存储在ThreadLocalMap对象维护的Entry类型数组 table内,下标为ThreadLocal对象和1111的与运算结果,entry的key为threadLocal对象
 
2.public Object 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();
    }

 

get方法,先获取当前线程的thread对象,再获取thread对象的threadLocalMap对象,然后根据当前的threadLocal对象取得table数组对应下标的Entry对象(因为存储的时候就是根据当前的ThreadLoca对象的Hash值存储的)
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);
        }

 

最后如果Thread对象的ThreadLocalMap为空的话,就调用setInitialValue方法,该方法初始化map并且放入null ( initialValue的返回值为null ),可以通过覆盖该方法修改没有set的时候的初始值
   
 private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
 

 

 
3.public void remove()方法
 public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

 

 
ThreadLocal的remove方法先获取当前线程的Map对象,然后调用Map的remove方法,删除entry
 
总结:
          对于非线程安全的变量可以将它封装进ThreadLocal中,在调用的时候不是直接引用而是使用ThreadLocal的set和get方法
          
          线程调用set方法:
          放入当前线程的ThreadLocalMap中,key为ThreadLocal的hashCode

以上是关于ThreadLocal总结(jdk1.8源码)的主要内容,如果未能解决你的问题,请参考以下文章

JDK1.8中ThreadLocal源码解析

Java -- 基于JDK1.8的ThreadLocal源码分析

java ThreadLocal使用

详细领悟ThreadLocal变量

并发编程:ThreadLocal从源码分析总结到内存泄漏

java题库总结3