关于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)