Android 消息循环机制之ThreadLocal 类详解

关于ThreadLocal 这个类大家可能用的比较少,但看过android 消息循环机制源码的朋友,应该看到过它,它在Looper 类中被使用到

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

被声明为Static 说明只有一份, final 修饰说明指向对象的引用也不可以修改了.

Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the same {@code ThreadLocal} object,but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. The implementation supports

官方的解释是:线程本地信息存储,每个线程都有各自的数据,所有的线程共享这一个ThreadLocal 对象,每个线程都可以从一个相同的对象中取出自己的数据.我们来看看到底是如何实现的.
先看 set 方法:

 public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }

该方法是用泛型实现,andorid 消息循环中传入的是Looper 对象,那么这里就以Looper 对象为例讲解
首先获得当前线程, 判断当前线程的value是否为空,如果为空,初始化当前线程.(初始化完毕后localValues 已经不为null)

Values initializeValues(Thread current) {
        return current.localValues = new Values();
    }

看一下Values 的构造函数


   /**
    * Constructs a new, empty instance.
    */
        Values() {
            initializeTable(INITIAL_SIZE);
            this.size = 0;
            this.tombstones = 0;
        }
  /**
   * Creates a new, empty table with the given capacity.
   */
        private void initializeTable(int capacity) {
            this.table = new Object[capacity * 2];
            this.mask = table.length - 1;
            this.clean = 0;
            this.maximumLoad = capacity * 2 / 3; // 2/3
        }

从中可以发现,每个Thread 都会有个Value,并且每个Value 在初始化的时候都会创建一个table ,table保存线程信息.当调用 values.put(this, value)时接着往下看:

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

上述代码实现了value 存入进了table [index+1]中.
ok,分析到这, 我们可以知道set方法本质就是将当前线程的value 存入到table 中.


接下来看 ThreadLocal 中的get 方法:

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

如果values 为 null,给当前线程的localValues 初始化Value, 默认get 方法返回的也是null

    protected T initialValue() {
        return null;
    }

如果values 不为null,那么直接从table[index + 1] 中直接取出. 而这个值恰好是我们调用set 添加进去的值.这就实现了将不同线程信息存入同一个ThreadLocal 对象,调用相同的get和set 方法时,取到的数据没有冲突的原因!!! ThreadLocal 会根据当前线程返回当前线程的数据!!!
源码就分析到这,我们看一个例子:

package com.example;
import java.util.ArrayList;
public class MyClass {
    static ThreadLocal<People> local = new ThreadLocal<People>();
    public static void main(String  args[]) {
        People p0 = new People();
        p0.name ="zhangsan";
        local.set(p0);
        System.out.println("ThreadMain p0 name: "+local.get().name+"");
        new Thread(){
            @Override
            public void run() {
                super.run();
                People p1 = new People();
                p1.name ="lisi";
                local.set(p1);
                System.out.println("SubThread1 p1 name: "+local.get().name+"");
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                super.run();
                People p2 = new People();
                p2.name = "wangwu";
                local.set(p2);
                System.out.println("SubThread2 p2 name: "+local.get().name+"");
            }
        }.start();
    }
}

class People {
    public String name;
}

hreadMain p0 name: zhangsan
SubThread2 p2 name: wangwu
SubThread1 p1 name: lisi

总结:
用户在使用时不需创建多个ThreadLocal 对象.ThreadLocal 类实现了根据不同线程,可以获得各自线程信息的功能.一个对象就可以搞定多个线程.

参考文献:(http://blog.csdn.net/singwhatiwanna/article/details/48350919)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值