【学习笔记】ThreadLocal

一、什么是ThreadLocal

首先,它是一个数据结构,有点像HashMap,可以保存”key : value”键值对,==但ThreadLocal只能保存一个==,并且各个线程的数据互不干扰。

1
2
3
ThreadLocal<String> localName = new ThreadLocal();
localName.set("HXC");
String name = localName.get();

实现原理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 添加数据
public void set(T value) {
// 1. 得到该线程的ThreadLocalMap
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
// 2. 如果不为空,就添加数据,否则则创建新的Map,并加入该数据。
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

// 得到数据
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();
}

ps:不明白为什么只存一个值,为什么要存在Map中

二、四种引用方式

  • 强引用:普通的引用。

    image-20200413135949094

  • 软引用:在内存不够的时候会回收。==适合做缓存。==

    1
    2
    // 新建SoftReferences对象,它软引用了byte数组。如果内存空间不够了,回收了软引用的对象。
    SoftReference<byte[]> softReference =new SoftReference<>(new byte[1024]);

    image-20200413140405506

  • 弱引用:遇到GC就会被回收。

    1
    WeakReference<M> m = new WeakReference<>(new M);

    image-20200413141033572

  • 虚引用:当虚引用指向的对象被回收后,会将通知信息发到队列中。==用于管理堆外内存(操作系统的内存)。==

    • 举例:假设请求一个网络资源,该资源存储在对外内存,使用GC去回收对外内存。
1
PhantomReference<Test> phantomReference = new PhantomReference<>(new Test(),MyQueue)

image-20200413141346747

三、解决内存泄漏,ThreadLocal和弱引用

1
2
3
4
5
// tl指向ThreadLocal,ThreadLocalMap弱引用指向ThreadLocal。
// Entry是弱引用。
// 1. 如果是强引用,即使tl=null,但key的引用依然指向ThreadLocal对象,所以会有内存泄漏。而弱引用则不会。
// 2. 但是还有内存泄漏存在,ThreadLocal被回收,key值变成null,导致整个value无法被访问到,因此会内存泄漏。当使用完了,一定要remove掉。
ThreadLocal<String> tl = new ThreadLocal<>();

image-20200413141033572

1
2
3
4
5
6
7
ThreadLocal<String> localName = new ThreadLocal();
try {
localName.set("XXX");
// 其它业务逻辑
} finally {
localName.remove();
}