一.基本介绍
谷歌开发文档上的介绍:Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the sameThreadLocal
object, but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. The implementation supportsnull
values.
中文意思是,实现了一个线程本地存储的功能,也就是说每个线程的相同的这个变量可以有不同的值,所有线程都用一个ThreadLocal对象,但是每个线程都能拿到不同的值,而且一个线程修改他的值不会影响其他线程中的值,这个值可以是null。
二.工作原理
ThreadLocal是一个线程内部的数据存储类,通过他可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。
(1).例子
public class MainActivity extends Activity {
private ThreadLocal<Boolean> mBoolThreadLocal = new ThreadLocal<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBoolThreadLocal.set(true);
System.out.println("xcqw0-" + Thread.currentThread().getName() + "--" + mBoolThreadLocal.get());
new Thread(){
@Override
public void run() {
super.run();
mBoolThreadLocal.set(false);
System.out.println("xcqw1-"+Thread.currentThread().getName()+"--"+mBoolThreadLocal.get());
}
}.start();
new Thread(){
@Override
public void run() {
super.run();
System.out.println("xcqw2-"+Thread.currentThread().getName()+"--"+mBoolThreadLocal.get());
}
}.start();
}
}
结果:
System.out﹕ xcqw0-main--true
System.out﹕ xcqw1-Thread-428--false
System.out﹕ xcqw2-Thread-429--null(没有赋值,所以是Null)
(2)工作原理
为啥访问同一个ThreadLocal的get方法,获取的值却不一样?一般都会有这样的疑问,不同的线程会根据不同的线程创建一个数据的载体Values(不同线程这个值肯定不一样),然后从中拿到数组和当前线程的索引,就可以获取对应的值,下面用源码验证一下。
/**
* 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(currentThread);
可以看出在Thread类的内部有一个成员
专门用于存储线程的ThreadLocal的数据就是 Values values,如果为null就对其初始化,然后再把ThreadLocal对象的值传递进去。
/**
* Gets Values instance for this thread and variable type.
*/
Values values(Thread current) {
return current.localValues;
}
Thread.java
/**
* Normal thread local values.
*/
ThreadLocal.Values localValues;
static class Values {
/**
* Size must always be a power of 2.
*/
private static final int INITIAL_SIZE = 16;
/**
* Placeholder for deleted entries.
*/
private static final Object TOMBSTONE = new Object();
可以看出Valus内部有一个数组table
/**
* Map entries. Contains alternating keys (ThreadLocal) and values.
* The length is always a power of 2.
*/
private Object[] table;
<span style="white-space:pre"> </span>。。。。。
<span style="white-space:pre"> </span>。。。。。
}
可下面是TheadLocal值得保存方法
/**
* 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的值将会被存储到
table数组
中,table[index+1] = value
下面是ThreadLocal的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);
}
总结:
从ThreadLocal的set和get方法可以看出,他们所操作的对象都是当前线程的localValues对象的table数据,因此在不同线程访问同一个ThreadLocal对象的get和set方法,他们对数据的读写操作都是发生在LocalValues(不同线程不一样)中,所有他们的操作都是在各自线程的内部, 这就是为什么TheadLocal可以在多个线程中可以互不干扰地存储和修改数据!