一, 是什么?怎么用?

是什么?

是每个线程的本地变量,能够存储每个线程独有的变量.

怎么用?

能够为每个线程创立一个独有的变量对象

能够实现线程间的数据隔离

Spring申明式事务中应用ThreadLocal实现数据库隔离


二, 类架构

ThreadLocal属性

/** * 该值用于给ThreadLocalHashMap中存入值时线性探测插入的bucket地位 */private final int threadLocalHashCode = nextHashCode();/** * 下一个要给出的hashCode,每次原子性更新,从0开始 */private static AtomicInteger nextHashCode = new AtomicInteger();/** * hashCode增值,应用这个数字能够使key平均的散布在2的幂次方的数组上 * 具体能够参考 https://www.javaspecialists.eu/archive/Issue164-Why-0x61c88647.html * 因为比较复杂,在这里不展开讨论 */private static final int HASH_INCREMENT = 0x61c88647;

ThreadLocalMap属性

/** * Map的初始化容量 */private static final int INITIAL_CAPACITY = 16;/** * 哈希表,长度始终是2的幂,起因在于2的幂-1的二进制全副为1 * 便于按位与操作 * 如16: 10000 - 1 = 1111 * 按位与后都会在数组中 */private Entry[] table;/** * 哈希表的长度 */private int size = 0;/** * 扩容阈值,默认为0,扩容大小为哈希表长度的2/3 */private int threshold;

三, 实现原理

1, 为什么ThreadLocal能够实现线程隔离?

/** * 创立一个ThreadLocal,能够看出在结构器外部并没有解决任何事件 */public ThreadLocal() {}/** * 给ThreadLocal中设置值会调用该办法 */public void set(T value) {    //获取以后线程    Thread t = Thread.currentThread();    //获取以后线程中的变量threadLocals    ThreadLocalMap map = getMap(t);    if (map != null)        map.set(this, value);    else        //以后线程的threadLocals变量为空,创立map设置值        createMap(t, value);}ThreadLocalMap getMap(Thread t) {    //获取t的线程变量    return t.threadLocals;}
由上能够看出给ThreadLocal设置值实质上是给Thread的本地变量threadLocals变量设置值,这就是为什么ThreadLocal能够实现线程之间数据隔离

2, 增删查操作

/** * 给ThreadLocal中设置值会调用该办法 */public void set(T value) {    //获取以后线程    Thread t = Thread.currentThread();    //获取以后线程中的变量threadLocals    ThreadLocalMap map = getMap(t);    if (map != null)        map.set(this, value);    else        //以后线程的threadLocals变量为空,创立map设置值        createMap(t, value);}void createMap(Thread t, T firstValue) {    //给线程设置变量值    t.threadLocals = new ThreadLocalMap(this, firstValue);}/** * 设置与key相干的值,key为ThreadLocal */private void set(ThreadLocal<?> key, Object value) {    Entry[] tab = table;    int len = tab.length;    //按位与获取插入的地位    int i = key.threadLocalHashCode & (len - 1);    /**     * 应用线性探测法插入值     * 从获取到的下标向后遍历,如果以后key等于数组中以后下标的key则间接批改值     * 如果数组以后下标地位为空则替换掉以后下标的entry     */    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;        }    }    //如果i向后没有与其雷同的key或者为空的地位则间接替换掉以后地位的entry    tab[i] = new Entry(key, value);    int sz = ++size;    //从i向后没有空位,并且数量大于阈值须要rehash,删除哈希表中某些为空的entry如果size大于阈值的3/4则须要扩容    if (!cleanSomeSlots(i, sz) && sz >= threshold)        rehash();}/** * 替换掉有效的entry */private void replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot) {    Entry[] tab = table;    int len = tab.length;    Entry e;    int slotToExpunge = staleSlot;    for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len))        //从后向前寻找空位,寻找到间隔staleSlot最远的一个地位        if (e.get() == null)            slotToExpunge = i;    // 向后遍历    for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {        ThreadLocal<?> k = e.get();        //找到了与ThreadLocal雷同的key,替换值        if (k == key) {            e.value = value;            //替换掉以后地位的entry            tab[i] = tab[staleSlot];            //替换entry            tab[staleSlot] = e;            // 后面没有空位,设置删除的地位            if (slotToExpunge == staleSlot)                slotToExpunge = i;            //删除前面为空的地位            cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);            return;        }        if (k == null && slotToExpunge == staleSlot)            slotToExpunge = i;    }    // 如果没有发现key,则间接替换    tab[staleSlot].value = null;    tab[staleSlot] = new Entry(key, value);    // staleSlot前有空位须要删除为空的entry    if (slotToExpunge != staleSlot)        cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);}/** * rehash哈希表 */private void rehash() {    //删除有效的entry并且rehash哈希表    expungeStaleEntries();    // 如果数量大于等于阈值的3/4,须要扩容    if (size >= threshold - threshold / 4)        resize();}/** * 删除有效的entry并且rehash哈希表 */private void expungeStaleEntries() {    Entry[] tab = table;    int len = tab.length;    for (int j = 0; j < len; j++) {        Entry e = tab[j];        if (e != null && e.get() == null)            expungeStaleEntry(j);    }}/** * 双倍扩容之前的哈希表 */private void resize() {    Entry[] oldTab = table;    int oldLen = oldTab.length;    int newLen = oldLen * 2;    Entry[] newTab = new Entry[newLen];    int count = 0;    for (int j = 0; j < oldLen; ++j) {        Entry e = oldTab[j];        if (e != null) {            //以后地位key为空,则间接GC            ThreadLocal<?> k = e.get();            if (k == null) {                e.value = null; // Help the GC            } else {                //获取新地位                int h = k.threadLocalHashCode & (newLen - 1);                //从以后地位向后寻找一个为空的地位                while (newTab[h] != null)                    h = nextIndex(h, newLen);                //从新插入entry                newTab[h] = e;                count++;            }        }    }    //从新设置阈值和哈希表属性    setThreshold(newLen);    size = count;    table = newTab;}/** * 删除某些entry以减半的形式删除 */private boolean cleanSomeSlots(int i, int n) {    boolean removed = false;    Entry[] tab = table;    int len = tab.length;    do {        i = nextIndex(i, len);        Entry e = tab[i];        if (e != null && e.get() == null) {            n = len;            removed = true;            i = expungeStaleEntry(i);        }        //无条件右移1位           /2    } while ((n >>>= 1) != 0);    return removed;}/** * 删除具体位置的entry,并且rehash之后的entry */private int expungeStaleEntry(int staleSlot) {    Entry[] tab = table;    int len = tab.length;    // 删除以后下标的entry    tab[staleSlot].value = null;    tab[staleSlot] = null;    size--;    Entry e;    int i;    //从以后地位向后遍历,寻找为空的或者rehash之后的entry    for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {        ThreadLocal<?> k = e.get();        if (k == null) {            e.value = null;            tab[i] = null;            size--;        } else {            int h = k.threadLocalHashCode & (len - 1);            //rehash之后的地位不是以后的地位须要删除以后的entry            if (h != i) {                //help GC                tab[i] = null;                //从h向后遍历,寻找一个为空的地位                while (tab[h] != null)                    h = nextIndex(h, len);                //插入entry                tab[h] = e;            }        }    }    return i;}
/** * 获取ThreadLocal中寄存的值 */public T get() {    //1, 获取以后线程    Thread t = Thread.currentThread();    //2, 获取以后线程中的threadLocals变量,也就是ThreadLocalMap对象    ThreadLocalMap map = getMap(t);    if (map != null) {        //3, 获取之前设置的值并返回        ThreadLocalMap.Entry e = map.getEntry(this);        if (e != null) {            @SuppressWarnings("unchecked")            T result = (T)e.value;            return result;        }    }    //3, 如果以后线程的threadLocals变量为空,则还没有初始化,须要进行初始化    return setInitialValue();}/** * 设置初始化值 */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;}/** * 初始化值为null */protected T initialValue() {    return null;}
/** * 删除变量 */public void remove() {    ThreadLocalMap m = getMap(Thread.currentThread());    if (m != null)        m.remove(this);}/** * 删除某个entry */private void remove(ThreadLocal<?> key) {    Entry[] tab = table;    int len = tab.length;    int i = key.threadLocalHashCode & (len - 1);    //从i向后遍历    for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {        if (e.get() == key) {            //help GC            e.clear();            //删除以后entry并且rehash前面的entry            expungeStaleEntry(i);            return;        }    }}

三, 存在问题

1, 内存透露问题

因为应用ThreadLocal实质上是应用ThreadLocalMap,在应用完ThreadLocal后,无奈手动删除ThreadLocalMap中的key(ThreadLocal的援用),所以可能会引起内存透露问题,然而代码在设计的时候就思考到了这一点,所以将ThreadLocalMap中的key(ThreadLocal)设置为了弱援用(WeakReference),即很容易被GC掉,但即便如此,咱们还是要在应用完后调用ThreadLocal的remove办法,手动删除ThreadLocal援用,防止内存透露.

2, 子线程不能拜访父线程变量

能够应用InheritableThreadLocal

原理

ThreadLocal<String> local = new InheritableThreadLocal<>();local.set("main local variable");public void set(T value) {    //获取以后线程    Thread t = Thread.currentThread();    //因为InheritableThreadLocal重写了ThreadLocal的getMap办法,所以上面调用的为InheritableThreadLocal中的getMap,获取的为Thread类的inheritableThreadLocals变量    ThreadLocalMap map = getMap(t);    if (map != null)        map.set(this, value);    else        //以后线程的threadLocals变量为空,创立map设置值        createMap(t, value);}ThreadLocalMap getMap(Thread t) {    return t.inheritableThreadLocals;}//创立子线程Thread thread = new Thread(() -> {    local.set("child thread variable");    System.out.println("child thread get local variable : " + local.get());});public Thread(Runnable target) {    init(null, target, "Thread-" + nextThreadNum(), 0);}private void init(ThreadGroup g, Runnable target, String name, long stackSize) {    init(g, target, name, stackSize, null, true);}private void init(ThreadGroup g, Runnable target, String name,                      long stackSize, AccessControlContext acc,                      boolean inheritThreadLocals) {    //获取以后线程,这里为父线程,子线程还没有被创立进去    Thread parent = currentThread();    ...        //这里的parent.inheritableThreadLocals在父线程set的时候曾经初始化了        if (inheritThreadLocals && parent.inheritableThreadLocals != null)            //所以子类的ThreadLocal也是应用的inheritableThreadLocals,不是之前的threadLocals            this.inheritableThreadLocals =            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);}static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {    return new ThreadLocalMap(parentMap);}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) {                //调用InheritableThreadLocal对象的childValue办法,返回e.value                Object value = key.childValue(e.value);                Entry c = new Entry(key, value);                //插入到子线程的inheritableThreadLocals,也就是将父线程的inheritableThreadLocals数据复制到子线程中                int h = key.threadLocalHashCode & (len - 1);                while (table[h] != null)                    h = nextIndex(h, len);                table[h] = c;                size++;            }        }    }}