今天分析ThreadLocal
先看源码注释
* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
*
get
or
set
method) has its own, independently initialized
* copy of the variable.
ThreadLocal
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变量。这些变量不同于普通副本,在每个线程中,它是一个独立的初始化拷贝。
Each thread holds an implicit reference to its copy of a thread-local
* variable as long as the thread is alive and the
ThreadLocal
* 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
uniqueNum
=
new
ThreadLocal() {
@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为空的话,会调用
点进去
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方法就只与当前线程有关,与其它线程的值无关