1.这个类是干嘛用的
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 {@code null} values.
以上是源代码中对该类的注释,我翻译一下:它能够保存一系列thread定义的值,一个ThreadLocal可以对应多个thread,多个thread可以共享一个Threadlocal,在ThreadLocal中一个thread修改其保存的值并不影响其他的thread的值。
下面我们来根据它具体的源代码来分析其作用。
2.它有什么功能
这个就有意思了,我们从源代码中看看它定义了哪些方法就能知道了,不过从1中我们大概可以猜出来它有保存数据的功能,那么就有存取数据的功能,以下我们从源代码中开始分析:
- 获取值的功能
/**
* 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.
*/
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
对象,在Values
对象不为空的情况下在values.table
数组中与出其下标,如果当前的ThreadLocal
弱引用对象和table
数组中指定下标的对象相等,就返回index+1
下标的table
对象;当values
对象为空时初始化values
对象,然后再返回table中的对象。
这里我们有几个问题总结下:
1.Values
对象是做什么用的
2.与出来的下标是怎么与出来的,为什么要通过与一个hash值来定义下标
3.为什么返回的对象是table数组的后一位
4.table数组是不是装的ThreadLocal对象
5.返回的是什么对象
6.values.getAfterMiss(this)
是怎么实现的
下面我们逐一解析:
1.Values对象
1.要了解一个类是做何用的看其注释最为直观,我们先看看它的注释:
/**
* Per-thread map of ThreadLocal instances to values.
*/
static class Values {…
注释的意思就是:每个线程的ThreadLocal实例和值组成的键值对,说实话我这种强行翻译有点拗口,但是意思已经到位了,看完后文你就明白这句注释的意思了。
2.我们再看看在ThreadLocal中是如何调用到的:
Values values = values(currentThread);
-----------------------------------------
/**
* Gets Values instance for this thread and variable type.
*/
Values values(Thread current) {
return current.localValues;
}
从以上代码可以看出Values
对象是在当前线程中定义的。
而ThreadLocal类中的set
方法为当前线程保存了TheadLocal
和value
值,如下所示:
/**
* 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.
*为当前线程设置它要存储的变量,如果这个变量为null值,那么该对象还是会被存储下来
*
* @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
类中的方法,在第13行代码中实际是初始化了Values
对象,而在第15行代码中则是将要存储的变量存储到Values
对象中去。
上面的代码我们才是真正地进入到了Values
类,而它其中的变量很重要(后面就会知道为什么了,因为确实用到很多),我们先来认识一下其中的变量:
/**
* Size must always be a power of 2.
*table的size
*/
private static final int INITIAL_SIZE = 16;
/**
* Placeholder for deleted entries.
*已被垃圾回收的对象的占位符
*/
private static final Object TOMBSTONE = new Object();
/**
* Map entries. Contains alternating keys (ThreadLocal) and values.
* The length is always a power of 2.
* 存储键值对用的table数组,就是value.put所存的地方
*/
private Object[] table;
/** Used to turn hashes into indices.
*用来与出table[]下标的数,其值为mask = table.length-1
*/
private int mask;
/** Number of live entries.
*未被回收的键值对数量
*/
private int size;
/** Number of tombstones.
*已被垃圾回收的数量
*/
private int tombstones;
/** Maximum number of live entries and tombstones.
*
*/
private int maximumLoad;
/** Points to the next cell to clean up.
*开始清除的下标位置
*/
private int clean;
了解了它的变量,回到之前的代码,put这个方法也很重要,我们来看看它到底做了什么:
/**
* 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;
}
}
}
这里先调用了cleanUp()方法,这个方法是为了记录哪些ThreadLocal对象已经被垃圾回收器回收,然后记录挂了的ThreadLocal数量和现存的ThreadLocal数量以及垃圾回收到的位置:tombstones++
,size--
,clean=index
;然后在第12行开始无限循环,我们看到table[]数组的偶数位存放的是ThreadLocal
的弱引用,或者为null,或者为TOMBSTONE;而奇数位存放的就是Object value
,也就是我们要保存的值;在15行,当key为ThreadLocal
的引用时,直接覆盖其对应的;而后面两个if()
判断语句就是当key为null
或者TOMBSTONE
时,直接将ThreadLocal.reference
和value
赋值给该table[index]、table[index+1],所以我认为这个方法其实可以直接改写成如下代码更为直观:
void put(ThreadLocal<?> key, Object value) {
...
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) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
if (k == TOMBSTONE) {
table[index] = key.reference;
table[index + 1] = value;
size++;
tombstones--;
return;
}
}
}
以上就是对put()
方法的分析。
cleanUp()源码及分析:
/**
* Cleans up after garbage-collected thread locals.
*当ThreadLocal对象被垃圾回收之后,需要将其键值对置为TOMBSTONE和null
*/
private void cleanUp() {
if (rehash()) {
// If we rehashed, we needn't clean up (clean up happens as
// a side effect).
return;
}
if (size == 0) {
// No live entries == nothing to clean.
return;
}
// Clean log(table.length) entries picking up where we left off
// last time.
int index = clean;
Object[] table = this.table;
for (int counter = table.length; counter > 0; counter >>= 1,
index = next(index)) {
Object k = table[index];
if (k == TOMBSTONE || k == null) {
continue; // on to next entry
}
// The table can only contain null, tombstones and references.
@SuppressWarnings("unchecked")
Reference<ThreadLocal<?>> reference
= (Reference<ThreadLocal<?>>) k;
if (reference.get() == null) {
// This thread local was reclaimed by the garbage collector.
table[index] = TOMBSTONE;
table[index + 1] = null;
tombstones++;
size--;
}
}
// Point cursor to next index.
clean = index;
}
这里第5行先调用了rehash()方法,然后就是判断table[]中是否有保存ThreadLocal
实例,没有就直接退出该方法,然后就是循环,将ThreadLocal
实例的弱引用被垃圾回收掉之后将其标志为TOMSTONE
和null,同时将成员变量tombstones++
,size--
,clean=index
。看到这里知道了这个方法再结合put()
方法我们知道了,这个方法主要就是为了将已被垃圾回收的ThreadLocal
对象所保存的位置清空以备后续重新置入键值对。第22行的next()
方法也让我学到了[很多][1]:
/**
* Gets the next index. If we're at the end of the table, we wrap back
* around to 0.
*当index大于table.length时返回的下标将从0开始
*/
private int next(int index) {
return (index + 2) & mask;
}
通过next()
方法源码就可以改写其循环为:
for(int counter = table.length;counter>0;counter>>1,index+=2){
...
}
这个循环其实是不完整循环,为什么说它是不完整循环呢,因为counter>0,counter>>1
限定了其循环次数,假如该table的长度为32,那么其最多循环的次数为6次,而其可存储的键值对的数量则为16对,显然无法循环完全,所以cleanUp()
方法里的循环是无法完整遍历的,这里为什么用不完整循环我还是不明白,如果有选手了解希望指点一下。
rehash()
源码及解析:
/**
* Rehashes the table, expanding or contracting it as necessary.
* Gets rid of tombstones. Returns true if a rehash occurred.
* We must rehash every time we fill a null slot; we depend on the
* presence of null slots to end searches (otherwise, we'll infinitely
* loop).
*/
private boolean rehash() {
if (tombstones + size < maximumLoad) {
return false;
}
int capacity = table.length >> 1;
// Default to the same capacity. This will create a table of the
// same size and move over the live entries, analogous to a
// garbage collection. This should only happen if you churn a
// bunch of thread local garbage (removing and reinserting
// the same thread locals over and over will overwrite tombstones
// and not fill up the table).
int newCapacity = capacity;
if (size > (capacity >> 1)) {
// More than 1/2 filled w/ live entries.
// Double size.
newCapacity = capacity * 2;
}
Object[] oldTable = this.table;
// Allocate new table.
initializeTable(newCapacity);
// We won't have any tombstones after this.
this.tombstones = 0;
// If we have no live entries, we can quit here.
if (size == 0) {
return true;
}
// Move over entries.
for (int i = oldTable.length - 2; i >= 0; i -= 2) {
Object k = oldTable[i];
if (k == null || k == TOMBSTONE) {
// Skip this entry.
continue;
}
// The table can only contain null, tombstones and references.
@SuppressWarnings("unchecked")
Reference<ThreadLocal<?>> reference
= (Reference<ThreadLocal<?>>) k;
ThreadLocal<?> key = reference.get();
if (key != null) {
// Entry is still live. Move it over.
add(key, oldTable[i + 1]);
} else {
// The key was reclaimed.
size--;
}
}
return true;
}
该方法主要作用为,当tombstones + size > maximumLoad
时,table[]数组的存储空间不够大了,需要扩展其length,就要重新初始化table[]数组和一些成员变量,所以调用到第32行代码初始化,而在这之前保存下当前table[]中已存储的变量Object[] oldTable = this.table;
,然后再通过从43行起的循环开始将旧table[]中的值赋值给新的table[]数组。
以上我们就已经分析完了Value类的存数据时所用的方法,回到一开始的ThreadLocal
获取其保存值的方法第23行中用到了Values类中的getAfterMiss()
方法,这个从方法名就可以看出是找不到该ThreadLocal
作为键时用的方法。
下面来看下getAfterMiss()
方法:
/**
* Gets value for given ThreadLocal after not finding it in the first
* slot.
*当第一个index下标中没有查找到该ThreadLocal的reference时,调用此方法将ThreadLocal的reference作为key值保存,该方法在ThreadLocal类中的get()方法中被调用
*/
Object getAfterMiss(ThreadLocal<?> key) {
Object[] table = this.table;
int index = key.hash & mask;
// If the first slot is empty, the search is over.
if (table[index] == null) {
Object value = key.initialValue();
// If the table is still the same and the slot is still empty...
if (this.table == table && table[index] == null) {
table[index] = key.reference;
table[index + 1] = value;
size++;
cleanUp();
return value;
}
// The table changed during initialValue().
put(key, value);
return value;
}
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
// Continue search.
for (index = next(index);; index = next(index)) {
Object reference = table[index];
if (reference == key.reference) {
return table[index + 1];
}
// If no entry was found...
if (reference == null) {
Object value = key.initialValue();
// If the table is still the same...
if (this.table == table) {
// If we passed a tombstone and that slot still
// contains a tombstone...
if (firstTombstone > -1
&& table[firstTombstone] == TOMBSTONE) {
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
// No need to clean up here. We aren't filling
// in a null slot.
return value;
}
// If this slot is still empty...
if (table[index] == null) {
table[index] = key.reference;
table[index + 1] = value;
size++;
cleanUp();
return value;
}
}
// The table changed during initialValue().
put(key, value);
return value;
}
if (firstTombstone == -1 && reference == TOMBSTONE) {
// Keep track of this tombstone so we can overwrite it.
firstTombstone = index;
}
}
}
第11行的key.initialValue();
:
/**
* Provides the initial value of this variable for the current thread.
* The default implementation returns {@code null}.
*用户可以继承`ThreadLocal`类然后重写该方法
* @return the initial value of the variable.
*/
protected T initialValue() {
return null;
}
getAfterMiss
方法做的任务跟put
方法差不多,只不过一个是取,一个是存了,操作的内容都是table
数组的变量修改和成员变量的赋值。
总结:Values
这个内部类就是为了当前线程在ThreadLocal中存储变量所用,那么这里又有一个问题了,为什么不直接用HashMap存储变量呢?如果有选手知道麻烦帮我解答一下。
2.与出来的下标是怎么与出来的,为什么要通过与一个hash值来定义下标
我们先看下在ThreadLocal中定义的hash
字段值是什么:
/**
* 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
是AutomaticInteger
类对象,它是线程安全的对象,它通过构造方法初始化成员变量value
为初始值,并通过getAndAdd()
方法传入增长步长来实现自动增长,后面我会出一篇关于AutomaticInteger
类的解析,这里先简单放一下它的源码调用:
/** Hash counter. */
private static AtomicInteger hashCounter = new AtomicInteger(0);
/**
* Creates a new AtomicInteger with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicInteger(int initialValue) {
value = initialValue;
}
/**
* Gets the current value.
*
* @return the current value
*/
public final int get() {
return value;
}
/**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the previous value
*/
public final int getAndAdd(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return current;
}
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
ThreadLocal中定义的变量hash是一个“魔数”,好像跟斐波拉契数列有关,这个原理我不懂,但是我也做了测试看Doug Lea’s Magic Number博客里的测试,它不是从0开始的循环,通过与mask的相与将会得到一个偶数index。
3.为什么返回的对象是table数组的后一位
因为table数组的偶数位存放的是ThreadLocal
的弱引用,在偶数位+1的奇数位存放当前thread的变量。他们以数组前后作为一个键值对来存放。
4.table数组是不是装的ThreadLocal对象
是的,而且还有当前thread的变量
5.返回的是什么对象
当前thread所存储的对象。
6.values.getAfterMiss(this)
是怎么实现的
看Values
类的解析
3.为什么需要这个类在Looper类中定义
1.保证了一个线程中只有一个Looper对象
4.未完成
1.AtomicInteger源码解析
2.了解ThreadLocal的其他应用
3.总结逻辑运算