android多线程数据存储 - ThreadLocal的工作原理

定义

ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,存储以后,只能在该线程中可以获取到存储的数据,对于其他线程来说无法获取。

使用场景

  1. 某些数据以线程为作用域并且在不同线程有不同的数据时。比如每个线程要使用Handler需要创建Looper,而且每个线程的Looper是不同的,所以使用ThreadLocal可以轻松的实现Looper在线程中的存取。
  2. 复杂逻辑下的对象传递。比如线程中的一个监听器,它需要贯穿线程的整个生命周期,这时就可以使用ThreadLocal让监听器作为线程中的全局变量而存在。

工作原理

ThreadLocal是一个泛型类,它的定义是public class ThreadLocal< T >,只要弄清楚它的get和set方法就能明白它的工作原理。

set方法的源码:

 /**
  * Sets the value of this variable for the current thread. If set to
  * {@code null}, the value will be set to null and the underlying entry will
  * still be present.
  *
  * @param value the new value of the variable for the caller thread.
  */
 public void set(T value) {
     Thread currentThread = Thread.currentThread();
     Values values = values(currentThread);
     if (values == null) {
         values = initializeValues(currentThread);
     }
     values.put(this, value);
 }

通过调用values方法

Values values(Thread current) {
    return current.localValues;
}

来得到存在Thread类里的localValues,然后调用put方法把ThreadLocal的值存储起来:

/**
 * Sets entry for given ThreadLocal to given value, creating an
 * entry if necessary.
 */
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;
         }
     }
 }

由源码可知,ThreadLocal的值是存在Values的一个内部数组table里,并且存储位置是在ThreadLocal的reference对象的存储位置的下一个位置。也就是说,ThreadLocal的reference对象和值都会存储到table数组里,如果reference的位置是index,那么值的位置索引就是index+1。


get方法的源码:

/**
 * Returns the value of this variable for the current thread. If an entry
 * doesn't yet exist for this variable on this thread, this method will
 * create an entry, populating the value with the result of
 * {@link #initialValue()}.
 *
 * @return the current value of the variable for the calling thread.
 */
@SuppressWarnings("unchecked")
public T get() {
    // Optimized for the fast path.
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values != null) {
        Object[] table = values.table;
        int index = hash & values.mask;
        if (this.reference == table[index]) {
            return (T) table[index + 1];
        }
    } else {
        values = initializeValues(currentThread);
    }

    return (T) values.getAfterMiss(this);
}

由源码可知,去get值时,是先找到reference的索引位置,然后再加1去得到值。如果找不到当前线程的Values,那么就返回initializeValues的值,这个方法我们可以自己覆盖:

 /**
  * Creates Values instance for this thread and variable type.
  */
 Values initializeValues(Thread current) {
     return current.localValues = new Values();
 }

demo

public class MainActivity extends ActionBarActivity {

    private static final String TAG = "ThreadLocal";

    private ThreadLocal<Integer> mThreadLocal = new ThreadLocal<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mThreadLocal.set(7);
        Log.d(TAG, "In Main Thread: " + mThreadLocal.get());

        new Thread(){
            @Override
            public void run() {
                mThreadLocal.set(6);
                Log.d(TAG, "In Sub Thread 1: " + mThreadLocal.get());
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                Log.d(TAG, "In Sub Thread 2: " + mThreadLocal.get());
            }
        }.start();
    }

}

输出的log为:

D/ThreadLocal﹕ In Main Thread: 7
D/ThreadLocal﹕ In Sub Thread 1: 6
D/ThreadLocal﹕ In Sub Thread 2: null

由于子线程2并没有设置mThreadLocal的值,所以get得到的是null。
由此验证了同一个ThreadLocal对象在各个线程间存储的值互不干扰,因为每一个线程有自己的ThreadLocal.Values。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值