Android ThreadLocal理解--续篇

还是ThreadLocal的一些遗留的疑问。
还是从Looper.java 的sThreadLocal变量说起。
static final ThreadLocal sThreadLocal = new ThreadLocal();
在Looper.java中这个sThreadLocal使用的是一个静态的最终变量,那么在调用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);
    }

这里调用的Values.put函数,传入this的key值就是我们的sThreadLocal。也就是说这个key永远都只会传sThreadLocal。

我们再结合 ThreadLocal 的两个变量,来看这个寻址的问题:

    /** Hash counter. */
    private static AtomicInteger hashCounter = new AtomicInteger(0);

    /**
     * Internal hash. We deliberately don't bother with #hashCode().
     * Hashes must be even. This ensures that the result of
     * (hash & (table.length - 1)) points to a key and not a value.
     *
     * We increment by Doug Lea's Magic Number(TM) (*2 since keys are in
     * every other bucket) to help prevent clustering.
     */
    private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);

这个hashCounter是一个静态变量,也就是说所有的线程都是用的这一个实例。而hash被final修饰,也就是同一个线程的同一个ThreadLocal实例只会产生一个值,不会再改变。
在looper的例子中,整个进程的所有线程都共用一个sThreadLocal。

因为index的值取决于hash,而且hash的值取决于hashCounter.getAndAdd(0x61c88647 * 2),0x61c88647 * 2是一个常量,最终取决于AtomicInteger.vaule:

    public final int getAndAdd(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return current;
        }
    }
get():
    public final int get() {
        return value;
    }

而AtomicInteger.vaule在这里,只有构造函数赋值为0,而后每次调用getAndAdd函数后,会更新一次值(累加(0x61c88647 * 2))。那就是说,在Looper中对于同一个线程,index是固定的,但对于几个线程来说,每个线程的index是在上一个线程的基础上计算出来的。
如:第一个线程调用sThreadLocal.set(new Looper(quitAllowed))时,这个Looper的实例被保存在当前线程的Values的0的位置,
第二个线在调用sThreadLocal.set(new Looper(quitAllowed))时,由于第一个线程已经将AtomicInteger.vaule值赋为(0x61c88647 * 2),所以最终计算出来的值为14。
后面是:28、10、24、6、20、2、16、30、12、8、22、4、18
由于默认mask默认是31,所以只能拿到15个数的散列分布的偶数序列。

我们写一个Demo来验证以上所述:
MainActivity.java:

public class MainActivity extends Activity {

    static int i=0;
    Intermediate intermediate=new Intermediate();

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

    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();

        intermediate.init("主线程");

        for (i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    intermediate.init("子线程"+String.valueOf(i));
                }
            }).start();

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

Intermediate.java

public class Intermediate {
    static final String TAG="ThreadLocaltest";
    static final ThreadLocal<ThreadValue> mThreadLocal = new ThreadLocal<ThreadValue>();
    public void init(String name) {
            ThreadValue threadValue = new ThreadValue(name);
            mThreadLocal.remove();
            mThreadLocal.set(threadValue);
            print();
            printLine();
    }

    public void print() {
        mThreadLocal.get().print();
    }

    public void printLine() {
        mThreadLocal.get().printLine();
    }

    static class ThreadValue {
        static final AtomicInteger hashCounter = new AtomicInteger(0);

        private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);

        private static int mask = 31;
        public String name;

        public ThreadValue() {

        }

        public ThreadValue(String name) {
            this.name = name;
        }

        public void print() {
            Log.d(TAG,"this = " + this + " name : " + this.name
                    + " , hashCounter :" + ((Object) hashCounter).hashCode()
                    + " , hashCounter's value :" + hashCounter
                    + " , hash :" + hash + " \n");

            Log.d(TAG,"hash&mask = " + (hash & mask) + " \n");
        }
        public void printLine()
        {
            Log.d(TAG,"......................................................................... \n");
        }
    }
}

输出:
03-25 11:48:45.315 D/ThreadLocaltest(20478): this = IntermediateThreadValue@787bbb0 name : 主线程 , hashCounter :169523753 , hashCounter’s value :-1013904242 , hash :0   
03-25 11:48:45.315 D/ThreadLocaltest(20478): hash&mask = 0   
03-25 11:48:45.315 D/ThreadLocaltest(20478): ……………………………………………………………….   
03-25 11:48:45.317 D/ThreadLocaltest(20478): this = Intermediate
ThreadValue@6cff1ae name : 子线程0 , hashCounter :169523753 , hashCounter’s value :-2027808484 , hash :-1013904242
03-25 11:48:45.317 D/ThreadLocaltest(20478): hash&mask = 14
03-25 11:48:45.317 D/ThreadLocaltest(20478): ……………………………………………………………….
03-25 11:48:46.317 D/ThreadLocaltest(20478): this = IntermediateThreadValue@cc11b4f name : 子线程1 , hashCounter :169523753 , hashCounter’s value :1253254570 , hash :-2027808484   
03-25 11:48:46.317 D/ThreadLocaltest(20478): hash&mask = 28   
03-25 11:48:46.317 D/ThreadLocaltest(20478): ……………………………………………………………….   
03-25 11:48:47.319 D/ThreadLocaltest(20478): this = Intermediate
ThreadValue@2a4e2dc name : 子线程2 , hashCounter :169523753 , hashCounter’s value :239350328 , hash :1253254570
03-25 11:48:47.320 D/ThreadLocaltest(20478): hash&mask = 10
03-25 11:48:47.320 D/ThreadLocaltest(20478): ……………………………………………………………….
03-25 11:48:48.319 D/ThreadLocaltest(20478): this = Intermediate$ThreadValue@f7bcfe5 name : 子线程3 , hashCounter :169523753 , hashCounter’s value :-774553914 , hash :239350328

结果与我们的推论是一样的。这样的好处是,在寻址的时候,可以把Values的obj当做hash表来用,查找的时候直接使用各个线程保存的hash做为下标,提取到的就是当前线程对应的数据。

因为我们的Values里的obj数组默认的大小是32也就是16个存储单元。
但是存储数据的单元却只能使用其中一个存储单元,这不是很浪费?
什么样的情况下才能使用obj数组的多个存储单元呢?
如果一个线程当中有多个ThreadLocal就会使用的到。这是因为在同一个线程中无论多少个ThreadLocal实例,都是使用同一个Values,因为在创建Values之前会判断current.localValues是否为空,如果不为空,就使用现成的。

但是在使用多个ThreadLocal时,有时候直接用保存的hash做为下标,找到的并不是对应的obj,这时候就需要继续往下遍历来查找了。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值