第26周——ThreadLocal详解

2018-07-25  本文已影响0人  阳光的nick_lxz

一 什么是ThreadLocal

ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程(哪里存的哪里才能取到)才可以获取到存在的数据,说白了,它的作用域就是线程,你现在线程A中存储了值,只能在线程A中取得到。
eg:在thread1中存入数据,只有在thread1中才能取到这个数据,在thread2等其他线程获取不到该数据。

public class Main2Activity extends AppCompatActivity {
    private static final String TAG = "Main2Activity";
    ThreadLocal<String> threadLocal;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        threadLocal = new ThreadLocal<>();
        threadLocal.set("主线程");

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                threadLocal.set("存储数据");
                Log.e(TAG, "run: " + threadLocal.get());
            }
        });
        thread.start();
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG, "run: " + threadLocal.get());
            }
        });
        thread2.start();
        Log.e(TAG, "run: " + threadLocal.get());
    }

}

打印结果

07-24 17:20:56.294 8091-8091/? E/Main2Activity: run: 主线程
07-24 17:20:56.294 8091-8104/? E/Main2Activity: run: 存储数据
07-24 17:20:56.295 8091-8105/? E/Main2Activity: run: null

通过上面的例子我们发现,明明用的是一个全局的变量threadLocal,但是3条日志内容却不一样。这就是threadLocal精华所在了。

二源码解析

先看set和get方法

set方法

 /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

可以看到,先拿到当前线程,,,,注意是当前线程(就是调用方法时候所在的线程),然后通过该线程拿到ThreadLocalMap 这个对象。。。它是个ThreadLocal的静态内部类。
源码注释是这样的

    /**
     * ThreadLocalMap is a customized hash map suitable only for
     * maintaining thread local values. No operations are exported
     * outside of the ThreadLocal class. The class is package private to
     * allow declaration of fields in class Thread.  To help deal with
     * very large and long-lived usages, the hash table entries use
     * WeakReferences for keys. However, since reference queues are not
     * used, stale entries are guaranteed to be removed only when
     * the table starts running out of space.
     */

就是本地线程存储数据的一个类,然后用这个对象去set值,key传的是当前线程,value就是内容了。
我们在进一步深入点进去map.set()方法

       /**
         * Set the value associated with key.
         *
         * @param key the thread local object
         * @param value the value to be set
         */
        private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            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;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

这里涉及到的算法就不管了,如果得到的K是就当前ThreadLocal对象,就赋值。

    if (k == key) {
                    e.value = value;
                    return;
                }

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对象,然后在获取ThreadLocalMap.Entry 取值。如果ThreadLocalMap为空则会返回空,setInitialValue()方法默认返回空的。

总结

ThreadLocal是一个线程本地变量,一个作用域在线程的变量,不同的线程设置和修改的ThreadLocal值互不影响
原理:ThreadLocal在get值和set值的时候都会先获取当前线程的ThreadLocalMap对象,ThreadLocalMap把ThreadLocal作为key来取值或者存值(相当于每个线程都有个ThreadLocal对象),这样对于每一个线程所存储的数据都是基于线程的,互不相干。

上一篇 下一篇

猜你喜欢

热点阅读