本文主要内容为: 1.演示 ThreadLocal 的用法 2.源码解析 3.ThreadLocal内存泄漏
一、ThreadLocal简介
ThreadLocal 类提供了局部的变量,方便同一个 Thread 在多个方法中获取这个变量,其他 Thread 无法获取到这个变量。 如果不使用 ThreadLocal ,也可以将一个上下文对象在多个方法中传递,也可以实现同样的功能,但是使用 ThreadLocal 能降低代码之间的耦合度,实现更加优雅。
可以将 ThreadLocal 理解为一个工具类,最终的 ThreadLocal.set() 的 value 是保存在对应 Thread 的 ThreadLocal.ThreadLocalMap threadLocals 字段中,ThreadLocalMap key 为 ThreadLocal,value 就是 ThreadLocal.set() 的value,同一个 Thread 可以保存多个不同的 ThreadLocal,多个不同的 Thread 也可以保存同一个 ThreadLocal,但是变量值相当于保存在每个 Thread 的本地内存,因而不存在线程安全问题,每个 Thread 操作的都是当前线程的 value,不会影响其他线程。
动动发财小手,关注 + 点赞 + 收藏不迷路。
二、ThreadLocal类图
使用 Intellij Idea 的 PlantUml 插件简单绘制了 ThreadLocal 类图,具体代码详见附录
如下图所示,ThreadLocalMap 是 ThreadLocal 的静态内部类,ThreadLocalMap 的 key 是一个弱引用,调用 ThreadLocal set() 或者 get() 方法,都是操作当前线程的 ThreadLocal.ThreadLocalMap threadLocals 这个map,其中 key 为 ThreadLocal,value 就是 set 的 value。
ThreadLocal类图如下:
三、ThreadLocal源码解析
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);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
static class ThreadLocalMap {
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
}
ThreadLocal 的 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();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
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;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
static class ThreadLocalMap {
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);
}
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
}
ThreadLocal 的 remove() 方法代码如下:
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
static class ThreadLocalMap {
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}
}
四、ThreadLocal的内存泄露
通过 ThreadLocal 类图,可以发现 Entry 的key是一个弱引用(WeakReference),当弱引用关联的对象只有弱引用与之关联,当 JVM 进行垃圾回收时,被弱引用关联的对象必定会被回收掉,此时 Entry.get() 就会为 null,但是如果 key 关联的对象是一个强引用,在 JVM 进行垃圾回收的时候,就不会被回收掉。例如弱引用关联强引用对象:ThreadLocal threadLocal = new ThreadLocal(); 只有在执行 threadLocal = null; 之后,Entry.get() 才会为 null。
当我们在线程run() 方法中new 一个 ThreadLocal ,当 run() 方法执行结束,ThreadLocal 生命周期也就结束了,此时 Entry.get() 为 null,但是 value 还是有值的,这就产生了内存泄漏,所以在线程执行完 set() 方法之后,要执行 remove() 方法。
其实在执行 ThreadLocal 的 set()、get()、remove() 方法时,ThreadLocal也会将 Entry key 为 null 对应的 value 置为 null,但是最好还是手动执行 remove() 方法,防止内存泄漏。
五、附录
- ThreadLocal类图 plantuml 代码如下:
@startuml
scale 160 width
interface Runnable {
run()
}
class Thread {
~ ThreadLocal.ThreadLocalMap threadLocals
~ ThreadLocal.ThreadLocalMap inheritableThreadLocals
run()
currentThread()
sleep(long millis)
start()
join(long millis)
interrupt()
}
class WeakReference {
}
class Entry {
~ Entry(ThreadLocal<?> k, Object v)
}
class ThreadLocalMap {
- int INITIAL_CAPACITY = 16
- Entry[] table
set(ThreadLocal<?> key, Object value)
getEntry(ThreadLocal<?> key)
remove(ThreadLocal<?> key)
}
class ThreadLocal {
set(T value)
get()
remove()
}
Runnable <|.. Thread : implements
WeakReference <|-- Entry : 继承
Entry <.. ThreadLocalMap : 依赖
ThreadLocalMap <-- Thread : 关联
ThreadLocalMap <.. ThreadLocal : 依赖
Thread <.. ThreadLocal : 依赖
'- private
'# protected
'~ package private
'+ public
'parent\n父类 <|-- child\n子类 : extend\n1.继承
'interface\n接口 <|.. achieve\n实现 : implements\n2.实现
'call\n被用方 <.. usage\n使用方 : dependency\n3.依赖
'owner\n主人 --> be_owned\n被拥有 : association\n4.1.关联
'A - B : association both\n4.2.双向关联
'overall\n整体 o--> members\n成员 : aggregation\n5.聚合(单向,关联关系的一种,与关联关系之间的区别是语义上的,关联的两个对象通常是平等的,聚合则一般不平等,有一种整体和局部的感觉,实现上区别不大)
'whole\n总体 *--> part\n部分 : composition\n6.组合
@enduml
引用: 1.https://www.cnblogs.com/fsmly/p/11020641.html 2.https://www.cnblogs.com/xiaozhuanfeng/p/10501093.html
|