ThreadLocal是Java提供的线程本地存储机制,可以实现多线程环境下数据的隔离。主要特点是:
- 每个线程都有自己的实例副本,实现了线程的数据隔离。ThreadLocal中存储的值对其他线程都不可见。
- 通过get()和set()来读写当前线程的实例副本,避免了线程安全问题。
- 本地线程副本通过弱引用持有,在线程消亡后可以自动释放,避免内存泄漏。
- 适用于将不安全的变量封装到线程内部、存储线程上下文信息等场景。
注意:
- 正确使用remove()释放数据,避免内存泄漏。
- 不要对继承ThreadLocal的变量进行修改,每个线程只会复制父类中的初始值。
- 避免使用static类型的ThreadLocal变量,这样会导致实例被所有线程共享。
- 过度使用ThreadLocal也会导致内存占用过高。
ThreadLocal适合对多线程环境中的变量进行线程隔离。但也需要注意避免内存泄漏等问题。
1.为什么可以实现线程的数据隔离?
Thread源码中有这样一句
ThreadLocal.ThreadLocalMap threadLocals = null;
也就是说,在Thread中引用了ThreadLocal中的ThreadLocalMap,
实际上,这也是为什么每一个线程可以进行数据的隔离,因为每一个线程都有自己的ThreadLocalMap,读写相关数据时都是在自己的ThreadLocalMap里面
现在我们来看一看ThreadLocal中的ThreadLocalMap的部分源码
Entry(ThreadLocal<?> k, Object v) { super(k); value = v; }
也就是说,实际上threadLocals存放的是<ThreadLocal,value>的键值对
因此,一个ThreadLocal对应一个的value值
如果想要存放多个属性,可以采用下面的方式
ThreadLocal<String>name=new ThreadLocal<>(); ThreadLocal<String>age=new ThreadLocal<>();
或者Map里存放Map
private static final ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<>();
使用如下
Map<String, Object> map = threadLocal.get(); map.put("name", "Alice"); map.put("age", 18);
2. 为什么get和set获得的是当前线程的ThreadLocals呢?
源码中的get()有这样的语句
Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
因为currentThread()的实现内部依赖于JVM的执行引擎,会根据真实的CPU代码执行状态来维护和返回currentThread。
我们可以认为此时获得的一定是当前的线程,通过该线程便可以获得对应的threadLocals.
3.Thread的threadLocals仍然可能出现内存泄漏
当用户没有手动remove(),
这时候Map会出现<null,value>这种无法访问但是又无法被回收的数据
key值为null因为其是弱引用,但是value是强引用所以不会为null,当用户没有手动remove时,value不会消失
如果用户又调用了set,get方法(ThreadLocal提供的对ThreadLocalMap数据操作的接口)
key为null的数据仍然会被回收,即使value不为空
但是当用户长时间没有调用方法并且这种键值对越来越多的时候,可能会导致内存泄漏
4.对于 ThreadLocal 变量,应该避免使用 static 静态类型
- static 静态变量会被所有线程共享,而 ThreadLocal 的设计目的就是为每个线程提供独立的变量副本。使用 static 会导致所有线程访问同一个变量,破坏 ThreadLocal 的线程隔离功能。
- 不同线程对 static 静态变量的访问存在竞争条件,需要额外的同步措施保证线程安全,而这违背了 ThreadLocal 简化线程安全的初衷。
- 每个线程对 static 静态变量的修改会互相影响,难以追踪问题。
- static 静态变量的生命周期与应用相同,使用不当容易造成内存泄漏。
所以为了发挥 ThreadLocal 的最大价值,应该定义非静态类型的 ThreadLocal 变量,通常是 private static 的实例变量或方法内部变量。
每个线程只能访问自己的副本,相互隔离,避免了静态变量的弊端。
如有错误,欢迎指出。
标签:变量,Thread,ThreadLocalMap,学习心得,ThreadLocal,线程,static From: https://www.cnblogs.com/leafstar/p/17632497.html