## 1 什么是 ThreadLocal
ThreadLocal 是什么呢?在理论开发中常常被用来绑定用户信息、日志号。数据库连贯等等。这样一来,咱们编码时就不必通过传递参数形式而影响业务逻辑。就如名字个别,咱们能够简略的认为它的作用就是 把数据绑定到以后线程上,而后用于后续的操作。
既然是将数据绑定到以后线程上,那最不便高效的数据存储形式就是 key-value 的 hash 形式存储了。不过不同于 HashMap 的实现形式,它独自提供了一个叫做 ThreadLocalMap 的 Map 类,与 HashMap 有着相似的性能,然而区别是它的 KEY 应用弱援用(只有 GC 扫描到,只有弱援用的状况下就会被回收)。
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {super(k);
value = v;
}
}
}
2 ThreadLocalMap 的 KEY 为什么设计成弱援用
为什么 ThreadLocalMap 和 HashMap 的 key 不同,会设计成弱援用呢?咱们来剖析一下:
- HashMap 被程序员应用存储各类数据,ThreadLocalMap 为动态拜访修饰符为 Default 的类,只为线程 Thread 存储数据(其属性)。
- 看一下类正文:To help deal withvery large and long-lived usages, the hash table entries use WeakReferences for keys。解释一下:为了帮忙解决微小和长时间存活的对象应用,才会应用弱援用。
所以总结下面两点,咱们就晓得。ThreadLocalMap 不像 HashMap 一样被内部应用,能够认为是线程公有的 Map,这就意味着:在线程长时间存活的状况下,如果 ThreadLocalMap 没有应用弱援用,而是应用 HashMap 的话。当 Map 中被放入大量大对象和值时,又不及时手动删除 K - V 的话就很可能会呈现利用堆栈溢出的状况。然而如果应用弱援用的话,那么在没有其余强援用的时候,就不须要程序员手动去删除 K -V, 再肯定水平上会升高堆栈溢出的危险(要是 KEY 都被内部强援用,那也没方法啊)。
3 ThreadLocal 为什么内存泄露
因为应用了弱援用,就有可能造成网上常常说的内存泄露?(其实感觉没有恐怖)咱们先说这个内存透露是怎么产生的呢?如下图:
图上咱们能够看到当 ThreadLocal 的实例如果设置为 null, 那么之后实例会被回收。这个时候 ThreadLocal, 也就是 ThreadLocalMap 的 key 仅有一个弱援用了,阐明 GC 时 KEY 会被回收。当回收后咱们就会发现 V 这个值就被留在了 Map 当中了,咱们无奈获取,也无奈删除。这就是所谓的 内存泄露 问题。
不过咱们也发现只有线程销毁后,ThreadLocalMap 也会被回收就解决了线程泄露的问题。然而如果线程长时间存活那就麻烦了。还有一种状况就是在应用线程池的时候。咱们都晓得线程池里的线程都是复用的,那么当设置了 ThreadLocal 的线程没有革除之前设置数据的话,就很可能造成之后复用线程的时候应用谬误数据。所以,ThreadLocal 类提供了一个解绑数据的办法 Remove 办法。
public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null) {m.remove(this);
}
}
4 总结
ThreadLocal 不便编程时将数据绑定到以后线程上,而不必办法传递参数,只需在须要应用时从 ThreadLocal 获取数据即可。ThreadLocal 中的 ThreadLocalMap 的 KEY 应用了弱援用,不便线程在长时间存活的状况下,及时清理 GC 只有弱援用的 KEY 值,肯定水平上升高堆栈溢出的危险。但同时因为弱援用的应用,带来了线程泄露的危险,以及在数据库线程池场景下应用造成数据谬误的危险。这就要求每个程序员在应用 ThreadLocal 完结后,及时应用 remove 办法(即便有些中央不 remove 也没有危险,然而写了就不会思考这思考那了,毕竟还有很多代码等着你)。