ThreadLocal详解

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 sThreadLocal = new ThreadLocal();
...略去
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 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();
    }
}

输出结果:


ThreadLocal详解_第1张图片

可以看到,

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

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

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

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

ThreadLocal 原理

ThreadLocal是一个泛型类:

public class ThreadLocal 

既然具有存储数据的功能,那么就会有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详解_第2张图片

你可能感兴趣的:(ThreadLocal详解)