java.lang.ThreadLocal
一、什么是ThreadLocal?
ThreadLocal为线程变量,意思是ThreadLoacl中填充的变量属于当前线程所有,该变量对于其他线程而言是隔离的
使用场景:
1. 在进行对象跨层传递时,使用ThreadLocal可以避免多次传递,打破层次间的约束;
2. 线程间数据隔离;
3. 进行事务操作,用于存储线程事务信息;
4. 数据库连接,Session会话管理。
二、ThreadLocal怎么用?
package
com.xs.thread.threadlocal
;
import
java.util.Random
;
import
java.util.concurrent.
TimeUnit
;
import
java.util.stream.
IntStream
;
/**
*
@author:
xusheng
*
@create-time
2020-08-19 21:31
**/
public class
ThreadLocalTest
{
public static void
main
(
String
[]
args
) {
Random
random
=
new
Random
()
;
IntStream
.range
(
0
,
5
)
.
forEach
(
a
->
new
Thread
(() -> {
ThreadLocal
<
String
>
local
=
new
ThreadLocal
<>
()
;
local
.
set
(
a
+
" "
+
random
.
nextInt
(
10
))
;
System
.
out
.
println
(
"
线程和
local
值分别为
"
+
local
.
get
())
;
try
{
TimeUnit
.
SECONDS
.
sleep
(
5
)
;
}
catch
(
InterruptedException
e
) {
e
.
printStackTrace
()
;
}
})
.
start
())
;
}
}
|
一般都在数据库连接时使用的比较多
三、ThreadLocal源码分析 (ThreadLocalMap中的key为ThreadLocal的实例变量)
1、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
)
;
}
|
首先获取到了当前线程t,然后调用getMap获取ThreadLocalMap,如果map存在,则将当前线程对象t作为key,要存储的对象作为value存到map里面去。如果该Map不存在,则初始化一个。
ThreadLocalMap
static class
ThreadLocalMap
{
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class
Entry
extends
WeakReference
<
ThreadLocal
<?>>
{
/** The value associated with this ThreadLocal. */
Object
value
;
Entry
(
ThreadLocal
<?>
k
,
Object
v
) {
super
(
k
)
;
value
=
v
;
}
}
|
我们可以看到ThreadLocalMap其实就是ThreadLocal的一个静态内部类,里面定义了一个Entry来保存数据,而且还是继承的弱引用。准确来说,是key为弱引用
2、get方法
public
T
get
() {
Thread
t
=
Thread
.currentThread
()
;
ThreadLocalMap
map
=
getMap
(
t
)
;
if
(
map
!=
null
) {
ThreadLocalMap
.
Entry
e
=
map
.
getEntry
(
this
)
;
if
(
e
!=
null
) {
@SuppressWarnings
(
"unchecked"
)
T
result
=
(
T
)
e
.
value
;
return
result
;
}
}
return
setInitialValue
()
;
}
|
首先获取当前线程,然后调用getMap方法获取一个ThreadLocalMap,如果map不为null,那就使用当前线程作为ThreadLocalMap的Entry的键,然后值就作为相应的的值,如果没有那就设置一个初始值。
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
;
}
|
3、总结
(1)每个Thread维护着一个ThreadLocalMap的引用
(2)ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
(3)ThreadLocal创建的副本是存储在自己的threadLocals中的,也就是自己的ThreadLocalMap。
(4)ThreadLocalMap的键值为ThreadLocal对象,而且可以有多个threadLocal变量,因此保存在map中
(5)在进行get之前,必须先set,否则会报空指针异常,当然也可以初始化一个,但是必须重写initialValue()方法。
(6)ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。