ThreadLocal使用场景及内存泄漏
定义
ThreadLocal官方定义: 该类提供线程局部变量。这些线程局部变量与普通变量的不同,每个线程都有自己独立初始化的变量副本(通过其get或set方法)。
如果希望将类的局部变量和线程状态关联(如 user id 或者 transcation id ), 就可以使用ThreadLocal.
如何使用它们
ThreadLocal的简单示例
private static ThreadLocal<String> threadLocal = ThreadLocal.withInitial(() -> "default");
public static void main(String[] args) throws InterruptedException {
threadLocal.set("in main class");
System.out.println("1 " + threadLocal.get());
ExecutorService service = Executors.newFixedThreadPool(1);
service.submit(() -> {
System.out.println("2 " + threadLocal.get());
threadLocal.set("in sub thread");
System.out.println("3 " + threadLocal.get());
});
service.shutdown();
Thread.sleep(500);
System.out.println("4 " + threadLocal.get());
}
//输出: 1 in main class
//2 null
//3 in sub thread
//4 in main class
从上面的示例可以看出, 在不同的线程中对ThreadLocal变量进行set和get操作时, 它们的变量是相互独立的.
为什么ThreadLocal能实现线程变量隔离
从源码中可知, 每一个Thread内部都有一个ThreadLocalMap, ThreadLocalMap里面 Key是当前ThreadLocal对象, Value是线程对应的变量副本, ThreadLocal所做的事情就是 负责维护每个Thread内部的ThreadLocalMap.
ThreadLocal的核心方法
T get(); // 获取本地线程变量
void set(T value) // 设置本地线程变量
void remove(); // 移除本地线程变量
get()的执行内容:
1. 根据Thread.currentThread() 获取当前执行线程信息, 并根据当前线程对象获取该线程的ThreadLocalMap对象
2. 若map不为空, 直接根据当前ThreadLocal对象获取entry, 进入过去value副本并返回
3. 若map为空, 将线程变量副本写入null, 并返回null.
set()的执行内容:
1. 根据Thread.currentThread() 获取当前执行线程信息, 并根据当前线程对象获取该线程的ThreadLocalMap对象
2. 若map不为空, 将ThreadLocal对象和值写入到当前线程对应的map中.
3. 若map为空, 则需要初始化当前线程的ThreadLocalMap对象, 再写入ThreadLocal和value的副本.
remove()的执行内容:
1. 获取当前线程的ThreadLocalMap并移除Key为当前ThreadLocal对象的键值对.
ThreadLocal的问题(可能内存泄漏)
当ThreadLocal存储了很多 Key = null 的Entry时, 但长时间不结束当前thread 且不再调用get(), set(), remove() 方法, 那么当ThreadLocal对象被回收后, Key是弱引用也被回收, 但是Value是强引用, 一直不回收, 就容易导致内存泄漏.
如何解决该问题: 每次调用完ThreadLocal之后, 都调用remove()方法, 手动销毁数据.
ThreadLocal带来的好处
- ThreadLocal把操作的数据存储在了线程本地, 直接本地内存读取, 不再需要synchronized等同步的方式修改主内存的数据, 然后再复制到本地内存, 提高了访问效率. 这也是为什么ThreadLocal能解决线程安全的问题.
ThreadLocal和线程同步的一些异同: 同步机制采用了“以时间换空间”的方式: 串行访问数据, 并共享对象. 而ThreadLocal采用了“以空间换时间”的方式: 并行访问,独享对象.
主要应用场景
- 在维护每个线程的Session的时候, ThreadLocal能帮助我们在每个线程中独享资源.
- 数据库连接在多线程读取的时候, 使用ThreadLocal包装, 能将线程和数据库连接绑定, 方便事务操作.
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 zhao4xi@126.com
文章标题:ThreadLocal使用场景及内存泄漏
文章字数:863
本文作者:Zhaoxi
发布时间:2018-12-23, 15:03:24
最后更新:2019-09-21, 15:24:44
原始链接:http://zhao4xi.github.io/2018/12/23/ThreadLocal使用场景及内存泄漏/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。