ThreadLocal
- java ThreadLocal类相当于提供了一种线程安全的简单方法,以此创建的内容只能在线程中存储和读取,其他线程无法访问到当前线程的变量。
threadLocal与synchronized的对比
- Synchronized是通过线程等待,通过加锁,用更长的时间换取变量共享,解决冲突。
- ThreadLocal是通过每个线程单独一份存储空间,用更大的空间换取来解决冲突。相比于synchronized,threadLocal的变量之间不存在共享的可能性,每个变量都是单独空间内隔离的内容,与synchronized的共享实现方式有所不同。
- 正是由于这种隔离的特殊性,threadLocal在一些场景下具有更好的使用效果。
threadLocal的一些api应用
创建threadLocal对象
private static ThreadLocal<T> tl1 = new ThreadLocal<>();
- T为所要存储的变量类型
给threadLocal变量赋值
tl1.set("th1===test");
获取threadLocal变量值
tl1.get()
移除threadLocal变量存储的值
tl1.remove()
threadLocal的底层存取原理
- 当调用
threadLocal.set(value)
方法时,通过阅读源码可以知道,将在底层调用线程的threadLocalMap
对象。以当前的threadLocal对象的弱引用作为key值,以传入的值作为value值,存储到threadLocalMap
对象中。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
- 而当读取时,直接调用
threadLocal.get()
方法,利用当前的threadLocal
对象,到threadLocalMap
中读取相应的值。
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();
}
threadLocal出现oom的原理及如何避免
-
threadLocal的回收原理主要是利用弱引用。对于同一个变量同时进行强引用和弱引用。当threadLocal对象调用结束,被垃圾回收强引用结束时,弱引用的
threadLocalMap
对应key会在下一次gc时被垃圾回收。这种情况下会使得threadLocalMap
存在key为null,value为实际存储值,且永远不会被回收导致内存泄漏或oom。 -
实际上,当线程结束时或调用
set(value)
、get()
方法时,会自动调用remove()
方法,便可自动避免内存的泄露。 -
因此实际上出现内存泄露的情况主要是使用线程池调用threadLocal,而线程使用结束key被置为空,同时不会被再次调用
set(value)
和get()
方法。因此可以轻易推断出,想要避免threadLocal的内存泄露问题,只需要在使用结束时手动调用remove()
方法,即可将value的引用释放,从根本上解决内存泄露的问题。
InheritableThreadLocal
- 该类继承于
ThreadLocal
类,与其每个线程拥有自己的threadLocal变量不同,该类允许当前线程于线程的子线程共同有权限访问InheritableThreadLocal
变量。
InheritableThreadLocal<String> t = new InheritableThreadLocal();
Thread t1 = new Thread(() -> {
t.set("test");
Thread t2 = new Thread(() -> {
String value = t.get();
});
});
Thread t3 = new Thread(() -> {
t.get();
});
- 线程t2为线程t1的子线程,因此可以直接读取t1线程所设置的t的值。而在t3线程中,由于于t1线程没有子线程的关系,因此无法读取t1线程中设置的t的值,所读取的值为null。
后记
- 千古兴亡多少事?悠悠。不尽长江滚滚流。