【架构师面试-JUC并发编程-10】-ThreadLocal

1:什么是ThreadLocal类,怎么使用它

 ThreadLocal为每个线程维护一个本地变量。

采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。

ThreadLocal类中维护一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本。

ThreadLocal在Spring中发挥着巨大的作用,在管理Request作用域中的Bean、事务管理、任务调度、AOP等模块都出现了它的身影。

Spring中绝大部分Bean都可以声明成Singleton作用域,采用ThreadLocal进行封装,因此有状态的Bean就能够以singleton的方式在多线程中正常工作了。

2:底层原理

Thread、ThreadLocal、ThreadLocalMap关系

在每个Thread对象中都持有一个ThreadLocalMap成员变量。

一个Thread对象中可能拥有多个ThreadLocal对象

ThreadLocalMap类也就是Thread.threadLocals

ThreadLocalMap类是每个线程Thread类里面的变量,里面最重要的是一个键值对数组Entry[] table,可以认为是一个map,键值对:

键:这个ThreadLocal 

值:实际需要的成员变量,如user或simpleDateFormat对象

【架构师面试-JUC并发编程-10】-ThreadLocal

首先 ThreadLocal 是一个泛型类,保证可以接受任何类型的对象。

因为一个线程内可以存在多个 ThreadLocal 对象,所以其实是 ThreadLocal 内部维护了一个 Map ,这个 Map 不是直接使用的 HashMap ,而是 ThreadLocal 实现的一个叫做 ThreadLocalMap 的静态内部类。而我们使用的 get()、set() 方法其实都是调用了这个ThreadLocalMap类对应的 get()、set() 方法。

3:内存泄露

1:什么是内存泄露

某个对象不再有用,但是占用的内存不能回收,导致OOM

2:内存泄露的原因

WeakReference是弱引用,将key用弱引用进行赋值。

弱引用的特点是,如果这个对象只被弱引用关联,没有任务强引用关联。这个对象就可以被GC回收。而value是强引用,GC不能回收。就会导致key被回收,而value还在。

【架构师面试-JUC并发编程-10】-ThreadLocal

 【架构师面试-JUC并发编程-10】-ThreadLocal

正常情况下,当线程终止,线程会回收,保存在ThreadLocal里的key和value会被垃圾回收。

但是如果线程不终止,那么key对应的value就不能被回收。(比如在线程池中创建的线程,这些线程会反复利用,很难终止)因此会有以下调用链

【架构师面试-JUC并发编程-10】-ThreadLocal

3:解决

在set、remove、rehash方法中会扫描key为null的Entry,并把对应的value设置为null,这样value对象就可以被回收。

【架构师面试-JUC并发编程-10】-ThreadLocal

 

但是如果一个ThreadLocal不被使用,则实际上set、remove、rehash方法也不会被调用,同时线程又不停止,则调用链一直存在,就会导致内存泄露。

阿里规约:在调用remove方法,就会删除对应的Entry对象,可以避免内存泄露,所以使用完ThreadLocal之后,就调用remove方法

【架构师面试-JUC并发编程-10】-ThreadLocal

 【架构师面试-JUC并发编程-10】-ThreadLocal

上一篇:细数线程池五大坑,一不小心线上就崩了


下一篇:最新Java笔试题分享,Java爬虫爬取视频