今天分析ThreadLocal
先看源码注释
* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized
* copy of the variable. <tt>ThreadLocal</tt> instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
大致意思是:这个类提供thread-local变量。这些变量不同于普通副本,在每个线程中,它是一个独立的初始化拷贝。
<p>Each thread holds an implicit reference to its copy of a thread-local
* variable as long as the thread is alive and the <tt>ThreadLocal</tt>
* instance is accessible; after a thread goes away, all of its copies of
* thread-local instances are subject to garbage collection (unless other
* references to these copies exist).
只要线程是存活的并且ThreadLocal对象可以访问,那么每个线程拥有一个隐式的引用,指向thread-local变量。当线程结束后,所有thread-local对象的拷贝都会受制于gc。
写一个使用ThreadLocal的范例
public class UniqueThreadIdGenerator {
private static AtomicInteger uniqueId = new AtomicInteger(0);
private static ThreadLocal<Integer> uniqueNum = new ThreadLocal<Integer>() {
@Override
// 如果当前线程是第一次请求id的分配则给它赋一个初始值
protected Integer initialValue() {
System.out.println("initalValue");
return uniqueId.getAndIncrement();
}
};
// 给当前线程返回它的id
public static int getCurrentThreadId() {
return uniqueNum.get();
}
// 设置当前线程的id
public static void setCurrentThreadId(int id) {
uniqueNum.set(id);
}
}
public class Thread1 implements Runnable{
@Override
public void run() {
// 线程的id是在它第一次run的时候才分配的,它run,它请求分配id,系统给它一个id
int id = UniqueThreadIdGenerator.getCurrentThreadId();
System.out.println(Thread.currentThread().getName() + " is running, its ID is: " + id);
// 三次向系统请求数据
for (int i = 0; i < 3; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is asking for data, my ID is:" + id);
}
System.out.println(Thread.currentThread().getName() + " is over!----------");
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Thread ta = new Thread(new Thread1(),"A");
Thread tb = new Thread(new Thread1(),"B");
Thread tc = new Thread(new Thread1(),"C");
ta.start();
tb.start();
tc.start();
}
运行结果:
07-04 05:43:44.873 12683-12701/com.xlz.dagger2_test I/System.out: initalValue
C is running, its ID is: 0
07-04 05:43:44.874 12683-12699/com.xlz.dagger2_test I/System.out: initalValue
07-04 05:43:44.875 12683-12699/com.xlz.dagger2_test I/System.out: A is running, its ID is: 1
07-04 05:43:44.883 12683-12700/com.xlz.dagger2_test I/System.out: initalValue
B is running, its ID is: 2
07-04 05:43:45.874 12683-12701/com.xlz.dagger2_test I/System.out: C is asking for data, my ID is:0
07-04 05:43:45.876 12683-12699/com.xlz.dagger2_test I/System.out: A is asking for data, my ID is:1
07-04 05:43:45.884 12683-12700/com.xlz.dagger2_test I/System.out: B is asking for data, my ID is:2
07-04 05:43:46.877 12683-12701/com.xlz.dagger2_test I/System.out: C is asking for data, my ID is:0
07-04 05:43:46.878 12683-12699/com.xlz.dagger2_test I/System.out: A is asking for data, my ID is:1
07-04 05:43:46.887 12683-12700/com.xlz.dagger2_test I/System.out: B is asking for data, my ID is:2
07-04 05:43:47.879 12683-12701/com.xlz.dagger2_test I/System.out: C is asking for data, my ID is:0
07-04 05:43:47.880 12683-12701/com.xlz.dagger2_test I/System.out: C is over!----------
07-04 05:43:47.882 12683-12699/com.xlz.dagger2_test I/System.out: A is asking for data, my ID is:1
07-04 05:43:47.883 12683-12699/com.xlz.dagger2_test I/System.out: A is over!----------
07-04 05:43:47.889 12683-12700/com.xlz.dagger2_test I/System.out: B is asking for data, my ID is:2
B is over!----------
可以看到,线程第一次使用ThreadLocal时会调用InitialValue方法
在获取线程相关的id时,是调用的ThreadLocal的get方法,我们以这个方法为入口,分析源码
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
get是返回当前线程的ThreadLocal变量的一个拷贝,如果这个变量没有值,则调用initalValue。
这里有个ThreadLocalMap,看下ThreadLocalMap是个啥
/**
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread. To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.
*/
static class ThreadLocalMap {
ThreadLocalMap是TheadLocal中的一个静态内部类,只适用于保存thread-local变量
每个Thead中都有一个ThreadLocalMap,看getMap(t)方法
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
就是获取Thread对象中的ThreadLocalMap成员
继续看get方法
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
如果map不为空,则从map中getEntry,看这个getEntry
private Entry getEntry(ThreadLocal key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
就是从这个table中获取value,这个table是一个数组,用来存放value的,这个table是ThreadLocalMap中的成员
key是ThreadLocal对象
那么这样一来,由于ThreadLocalMap是每个线程独有的,即便ThreadLocal的相同,即key相同,但是table不同,取到的value也是线程独有的
上面分析的是map不为空的情况,如果map为空的话,会调用
setInitialValue();
点进去
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
这里先调用initValue()方法
protected T initialValue() {
return null;
}
initialValue方法是protected方法,这里默认返回null
可以在子类中重写这个方法,给ThreadLocal变量给一个初值
如果map为null,则调用createMap()
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
可以看到,就是给Thread里面new了一个ThreadLocalMap
get方法的分析就到这里,可以知道ThreadLocal的get方法其实就是从Thead中ThreadLocalMap中获取value,每个Thread的ThreadLocalMap是不同,所以ThreadLocal获取的value也是每个线程独立的值
下面分析ThreadLocal的set方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
这里可以看到,set方法是设置当前线程的TheradLocalMap里面的value,那么这个set方法就只与当前线程有关,与其它线程的值无关