ThreadLocal为每个使用它的线程提供一个变量的副本。
ThreadLocal中的主要方法:
- 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