美文网首页Android高级进阶
2019-08-10-Handler机制之ThreadLocal

2019-08-10-Handler机制之ThreadLocal

作者: 王元 | 来源:发表于2019-08-10 21:32 被阅读0次

    ThreadLocal顾名思义就是线程本地变量

    1,ThreadLocal介绍

    线程局部变量,就是每个线程都有一个自己独一的变量副本,举个例子,大家就能明白

    ThreadLocal<String> tl = new ThreadLocal<>();
    tl.set("main");
    
    Thread a = new Thread() {
        @Override
        public void run() {
            super.run();
            tl.set("A");
            System.out.println("a = [" + tl.get() + "]");
        }
    };
    
    Thread b = new Thread() {
        @Override
        public void run() {
            super.run();
            tl.set("B");
            System.out.println("b = [" + tl.get() + "]");
        }
    };
    
    a.start();
    b.start();
    System.out.println("main = [" + tl.get() + "]");
    

    上面代码的输出结果是

    a = [A]
    main = [main]
    b = [B]
    

    如上可以看出,虽然不同的线程访问了同一个对象tl,但是它们通过tl获取到的对象却是不同的,也就是说ThreadLocal为每个线程保存了一个副本

    2,ThreadLocal的原理

    1,set的原理

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    

    从上面的源码可以看出,ThreadLocal.set方法的本质是先通过当前线程获取到当前线程的ThreadLocalMap,然后调用ThreadLocalMap.set(key, value)方法,如果为空的情况下,使用当前线程来创建ThreadLocalMap,如下

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    

    getMap源码如下:

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    

    如上可以看出,ThreadLocalMap是线程的属性之一,因此它是和当前线程相关的

    2,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();
    }
    

    如上可以看出,尝试从ThreadLocalMap中获取,如果没有获取的,则返回默认null

    3,ThreadLocal.ThreadLocalMap原理

    static class ThreadLocalMap {
    
        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;
    
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
    
        /**
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;
    
        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;
    
        /**
         * The number of entries in the table.
         */
        private int size = 0;
    
        /**
         * The next size value at which to resize.
         */
        private int threshold; // Default to 0
    
        /**
         * Set the resize threshold to maintain at worst a 2/3 load factor.
         */
        private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }
    
        /**
         * Construct a new map initially containing (firstKey, firstValue).
         * ThreadLocalMaps are constructed lazily, so we only create
         * one when we have at least one entry to put in it.
         */
        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);
        }
    }
    

    从上面的源码可以看出,ThreadLocalMap内部仅仅维护了Entry[] table,数组。其中Entry实体中对应的key为弱引用(下文会将为什么会用弱引用),在第一次放入数据时,会初始化数组长度(为16),定义数组扩容阀值(当前默认数组长度的2/3)。

    如果key使用强引用,那么当引用ThreadLocal的对象被回收了,但ThreadLocalMap中还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致内存泄漏。

    弱引用的key也可能带来一些问题,就是ThreadLocalMap可能有很多null为key的数据,而此时的value依然是被强引用,因此

    注意:不管是调用ThreadLocal的set()还是get()方法,都会去清除key==null的数据。

    • ThreadLocal本质是操作线程中ThreadLocalMap来实现本地线程变量的存储的
    • ThreadLocalMap是采用数组的方式来存储数据,其中key(弱引用)指向当前ThreadLocal对象,value为设的值
    • ThreadLocal为内存泄漏采取了处理措施,在调用ThreadLocal的get(),set(),remove()方法的 时候都会清除线程ThreadLocalMap里所有key为null的Entry
    • 在使用ThreadLocal的时候,我们仍然需要注意,避免使用static的ThreadLocal,分配使用了ThreadLocal后,一定要根据当前线程的生命周期来判断是否需要手动的去清理ThreadLocalMap中清key==null的Entry。

    ThreadLocal详细介绍

    相关文章

      网友评论

        本文标题:2019-08-10-Handler机制之ThreadLocal

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