美文网首页
ThreadLocal

ThreadLocal

作者: modou1618 | 来源:发表于2019-01-23 19:59 被阅读0次

    一 初始化

    • 变量hashcode计算,map中存储时用于计算桶索引
    private final int threadLocalHashCode = nextHashCode();
    
    private static AtomicInteger nextHashCode =
            new AtomicInteger();
    
    private static final int HASH_INCREMENT = 0x61c88647;
    
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
    
    • get时判断为null,则set一个新值
    private static ThreadLocal<Random> localRandom3 = new ThreadLocal<Random>();
    public Random get() {
        Random random = localRandom3.get();
        if (random == null) {
            random = new Random();
            localRandom3.set(random);
        }
    
        return random;
    }
    
    • 重写initialValue()
    private static ThreadLocal<Random> localRandom2 = new ThreadLocal<Random>(){
        @Override
        protected Random initialValue() {
            return new Random();
        }
    };
    
    • 使用withInitial(),内部新建类SuppliedThreadLocal,重写initialValue()
    private static ThreadLocal<Random> localRandom = ThreadLocal.withInitial(()->new Random());
    
    public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
        return new ThreadLocal.SuppliedThreadLocal<>(supplier);
    }
    
    static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
    
        private final Supplier<? extends T> supplier;
    
        SuppliedThreadLocal(Supplier<? extends T> supplier) {
            this.supplier = Objects.requireNonNull(supplier);
        }
    
        @Override
        protected T initialValue() {
            return supplier.get();
        }
    }
    

    二 set()

    • 获取线程对象中存储线程变量的map
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    
    ThreadLocal.ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    

    2.1 map不存在

    • 新建createMap(t, value);
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue);
    }
    
    • ThreadLocalMap实例化.
      初始化存储数组
      初始化阈值
      按hashcode计算索引,存储变量
    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
        table = new ThreadLocal.ThreadLocalMap.Entry[INITIAL_CAPACITY];
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);
        size = 1;
        setThreshold(INITIAL_CAPACITY);
    }
    

    2.2 map已存在

    • 插入值map.set(this, value);
    • int i = key.threadLocalHashCode & (len-1);计算索引
    • 遍历存储表,索引按((i + 1 < len) ? i + 1 : 0)顺序增加。
    • k相同,则更新值
    ThreadLocal<?> k = e.get();
    if (k == key) {
        e.value = value;
        return;
    }
    
    • k被回收,则替换已回收的索引
    if (k == null) {
        replaceStaleEntry(key, value, i);
        return;
    }
    
    • 找到一个空索引,则赋值tab[i] = new Entry(key, value);
    • 遍历存储表清除已回收的索引cleanSomeSlots
    private boolean cleanSomeSlots(int i, int n) {
        boolean removed = false;
        ThreadLocal.ThreadLocalMap.Entry[] tab = table;
        int len = tab.length;
        do {
            i = nextIndex(i, len);
            ThreadLocal.ThreadLocalMap.Entry e = tab[i];
            if (e != null && e.get() == null) {
                n = len;
                removed = true;
                i = expungeStaleEntry(i);
            }
        } while ( (n >>>= 1) != 0);
        return removed;
    }
    
    • 若无回收索引,rehash(),则再次清除回收索引后,扩容2倍,重新计算所有存储对象的存储位置。

    2.2.1 replaceStaleEntry

    • 查找是否有空索引,进行清理cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
    • 存储线程变量tab[staleSlot] = new Entry(key, value);

    2.2.2 expungeStaleEntry(i)

    • 释放回收的对象
    tab[staleSlot].value = null;
    tab[staleSlot] = null;
    size--;
    
    • 从回收索引开始遍历存储表,回收引用则释放对象
    ThreadLocal<?> k = e.get();
    if (k == null) {
        e.value = null;
        tab[i] = null;
        size--;
    } 
    
    • 非回收索引则使用hash计算索引,从初始索引开始重新定位存储索引
    int h = k.threadLocalHashCode & (len - 1);
    if (h != i) {
        tab[i] = null;
    
        // Unlike Knuth 6.4 Algorithm R, we must scan until
        // null because multiple entries could have been stale.
        while (tab[h] != null)
            h = nextIndex(h, len);
        tab[h] = e;
    }
    

    三 get()

    • 获取存储对象ThreadLocalMap map = getMap(t);
    • map存在,则根据索引冲突规则,查找目标对象
    • map不存在,调用初始化函数initialValue();获取初始化对象,插入ThreadLocalMap或初始化ThreadLocalMap
    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;
    }
    

    四 remove()

    • 获取ThreadLocalMap
    • 按索引规则找到目标对象,删除
    • 清除已回收对象,按索引规则对目标对象索引之后的对象重新计算存储位置
    public void remove() {
        ThreadLocalMap m = getMap(Thread.currentThread());
        if (m != null)
            m.remove(this);
    }
    private void remove(ThreadLocal<?> key) {
        ThreadLocal.ThreadLocalMap.Entry[] tab = table;
        int len = tab.length;
        int i = key.threadLocalHashCode & (len-1);
        for (ThreadLocal.ThreadLocalMap.Entry e = tab[i];
             e != null;
             e = tab[i = nextIndex(i, len)]) {
            if (e.get() == key) {
                e.clear();
                expungeStaleEntry(i);
                return;
            }
        }
    }
    

    五 ThreadLocalMap存储格式

    • 使用弱引用存储存储key
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;
    
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    
    • 网上看的对引用的介绍

    弱引用对象的存在不会阻止它所指向的对象被垃圾回收器回收。弱引用最常见的用途是实现规范映射(canonicalizing mappings,比如哈希表)。
    假设垃圾收集器在某个时间点决定一个对象是弱可达的(weakly reachable)(也就是说当前指向它的全都是弱引用),这时垃圾收集器会清除所有指向该对象的弱引用,然后把这个弱可达对象标记为可终结(finalizable)的,这样它随后就会被回收。与此同时或稍后,垃圾收集器会把那些刚清除的弱引用放入创建弱引用对象时所指定的引用队列(Reference Queue)中。

    实际上,Java中存在四种引用,它们由强到弱依次是:强引用、软引用、弱引用、虚引用。下面我们简单介绍下除弱引用外的其他三种引用:
    强引用(Strong Reference):通常我们通过new来创建一个新对象时返回的引用就是一个强引用,若一个对象通过一系列强引用可到达,它就是强可达的(strongly reachable),那么它就不被回收
    软引用(Soft Reference):软引用和弱引用的区别在于,若一个对象是弱引用可达,无论当前内存是否充足它都会被回收,而软引用可达的对象在内存不充足时才会被回收,因此软引用要比弱引用“强”一些
    虚引用(Phantom Reference):虚引用是Java中最弱的引用,那么它弱到什么程度呢?它是如此脆弱以至于我们通过虚引用甚至无法获取到被引用的对象,虚引用存在的唯一作用就是当它指向的对象被回收后,虚引用本身会被加入到引用队列中,用作记录它指向的对象已被回收。

    相关文章

      网友评论

          本文标题:ThreadLocal

          本文链接:https://www.haomeiwen.com/subject/flojjqtx.html