ThreadLocal
ThreadLocal 不是用来解决共享对象的多线程访问问题的,ThreadLocal只适用于 共享对象会造成线程安全的业务场景
ThreadLocalMap
ThreadLocalMap是ThreadLocal的静态内部类,数据存放在ThreadLocalMap.Entry里面。
thread,threadLocal,threadLocalMap,Entry之间的关系:
Tips:实线表示强引用,虚线表示弱引用
需要注意的是Entry中的key是弱引用,当threadLocal外部强引用被置为null(threadLocalInstance=null),那么系统 GC 的时候,根据可达性分析,这个threadLocal实例就没有任何一条链路能够引用到它,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏。
Tips:在threadLocal的生命周期里,针对threadLocal存在的内存泄漏的问题,都会通过expungeStaleEntry,cleanSomeSlots,replaceStaleEntry这三个方法清理掉key为null的脏entry。
ThreadLocal的使用场景
ThreadLocal 不是用来解决共享对象的多线程访问问题的,数据实质上是放在每个thread实例引用的threadLocalMap,也就是说每个不同的线程都拥有专属于自己的数据容器(threadLocalMap),彼此不影响。因此threadLocal只适用于 共享对象会造成线程安全 的业务场景。
ThreadLocal正确的使用方法
- 每次使用完ThreadLocal都调用它的remove()方法清除数据
- 将ThreadLocal变量定义成private static,这样就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉。
弱引用(WeakReference)
例子1:弱引用被回收
/**
* 弱引用
*/
@Test
void weakReferenceTest() {
User user = new User();
WeakReference<User> userWeakReference = new WeakReference<User>(user);
for (int i = 0; ; i++) {
if (userWeakReference.get() != null) {
System.out.println("对象还存活" + i + userWeakReference);
} else {
System.out.println("对象已被回收!");
break;
}
}
}
例子2:弱引用没有被回收
/**
* 弱引用
*/
@Test
void weakReferenceTest() {
User user = new User();
WeakReference<User> userWeakReference = new WeakReference<User>(user);
for (int i = 0; ; i++) {
System.out.println("User强引用:" + user);
if (userWeakReference.get() != null) {
System.out.println("对象还存活" + i + userWeakReference);
} else {
System.out.println("对象已被回收!");
break;
}
}
}
两个例子只相差一行,在循环中使用了强引用对象,则弱引用没被回收。
总结
- key使用强引用
当ThreadLocalMap的key使用强引用时,此时若是外部的ThreadLocal对象被置为null,按理说应该被回收,但是ThreadLocalMap中还持有对ThreadLocal的强引用,如果没有手动删除,那么ThreadLocal不会被回收,导致Entry内存泄漏。
- key使用弱引用
当ThreadLocalMap的key为弱引用回收ThreadLocal对象时,由于ThreadLocalMap只持有ThreadLocal的弱引用,即使没有手动删除,也不会影响ThreadLocal的回收。当key为null时,在下一个调用ThreadLocalMap的set、get、remove方法时会清除value值。