还是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 = IntermediateThreadValue@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 = IntermediateThreadValue@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,这时候就需要继续往下遍历来查找了。