ThreadLocal详解

作者: 行走的老者 | 来源:发表于2016-10-14 13:12 被阅读243次

    ThreadLocal说明

    ThreadLocal是一个线程内部的数据存储类,使用它来保存数据,只有当前的线程才可以访问,其他线程无法访问到其存储的数据,这个在某些场景下是非常有用的。比如:Android 里面Looper,Handler机制,对于Handler来说,要获取到线程里面的Looper,就必须使用ThreadLocal来存储,否则无法拿到指定的Looper,这个在源码中也有所体现:

    //1 Handler.java
    public Handler(Callback callback, boolean async) {
        ...略去
        //获取looper
        mLooper = Looper.myLooper(); 
           ...略去
    }
    
    //2 Looper.java
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    ...略去
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //存储
        sThreadLocal.set(new Looper(quitAllowed));
    }
    
     public static Looper myLooper() {
        //获取存储的Looper
        return sThreadLocal.get();
    }
    

    ThreadLocal 使用小demo

    public class MainActivity extends AppCompatActivity {
    
        @BindView(R.id.btn_text)
        Button mBtnText;
    
        private ThreadLocal<Boolean> mBooleanThreadLocal = new ThreadLocal<>();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
            Logger.init("liao").logLevel(LogLevel.FULL);
    
        }
    
        @OnClick(R.id.btn_text)
        public void textClick() {
            mBooleanThreadLocal.set(true);
            Logger.d("[Thread#main]mBooleanThreadLocal= %s", mBooleanThreadLocal.get());
    
            new Thread("Thread#1") {
                @Override
                public void run() {
                    mBooleanThreadLocal.set(false);
                    Logger.d("[Thread#1]mBooleanThreadLocal= %s", mBooleanThreadLocal.get());
                }
            }.start();
    
            new Thread("Thread#2") {
                @Override
                public void run() {
                    Logger.d("[Thread#2]mBooleanThreadLocal= %s", mBooleanThreadLocal.get());
                }
            }.start();
        }
    }
    

    输出结果:



    可以看到,

    第一个在主线程设置为true,所以获取到的为true;

    第二个我们设置了为false,所以获取到的为false;

    第三个我们没有赋值,所以获取到的为null。

    从上面的日志我们可以看出,虽然我们访问的是同一个对象,但是获取到的值是不一样的,这个就是ThreadLocal的神奇之处,在某些场景下我们可以实现很复杂的功能。

    ThreadLocal 原理

    ThreadLocal是一个泛型类:

    public class ThreadLocal<T> 
    

    既然具有存储数据的功能,那么就会有get,set等方法,所以我们只需弄懂这些方法,那么ThreadLocal的原理也就明白了。

    首先我们看下Thread类:

    //1. Thread.java
    public class Thread implements Runnable {
        ...
        //专门用于存储线程的ThreadLocal的数据
         ThreadLocal.Values localValues;
        ...
    }
    

    再来看下ThreadLocal类的set方法:

    public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            //为null,则初始化一个Values值
            values = initializeValues(currentThread);
        }
        //存储ThreadLocal值
        values.put(this, value);
    }
    

    下面我们来看Values类,此类提供存储ThreadLocal值的设置:

    static class Values {
        ...
        //存储ThreadLocal值
         private Object[] table;
        ...
        
        // 具体存储ThreadLocal算法:
        //ThreadLocal的值在table数组中的存储位置总是为
        //ThreadLocal的reference字段所标识的对象的下一个位置,
        //比如ThreadLocal的reference对象在table数组的索引为
        //index,那么ThreadLocal的值在table数组中的索引就是index+1。
        //所以最终ThreadLocal的值将会被存储在table数组中:table[index + 1] = value。
        void put(ThreadLocal<?> key, Object value) {
            cleanUp();
    
            // Keep track of first tombstone. That's where we want to go back
            // and add an entry if necessary.
            int firstTombstone = -1;
    
            for (int index = key.hash & mask;; index = next(index)) {
                Object k = table[index];
    
                if (k == key.reference) {
                    // Replace existing entry.
                    table[index + 1] = value;
                    return;
                }
    
                if (k == null) {
                    if (firstTombstone == -1) {
                        // Fill in null slot.
                        table[index] = key.reference;
                        table[index + 1] = value;
                        size++;
                        return;
                    }
    
                    // Go back and replace first tombstone.
                    table[firstTombstone] = key.reference;
                    table[firstTombstone + 1] = value;
                    tombstones--;
                    size++;
                    return;
                }
    
                // Remember first tombstone.
                if (firstTombstone == -1 && k == TOMBSTONE) {
                    firstTombstone = index;
                }
            }
        }
    }
    

    最后再来看下ThreaLocal的get方法:

    public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            //获取存储ThreadLocal数据值
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                //获取值ThreadLoad索引的下一个
                return (T) table[index + 1];  
            }
        } else {
            values = initializeValues(currentThread);
        }
    
        return (T) values.getAfterMiss(this);
    }
    

    最后附上一张类图:

    相关文章

      网友评论

        本文标题:ThreadLocal详解

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