ThreadLocal使用场景
ThreadLocal 用作保存每个线程独享的对象,为每个线程都创建一个副本,这样每个线程都可以修改自己所拥有的副本, 而不会影响其他线程的副本,确保了线程安全。
ThreadLocal 用作每个线程内需要独立保存信息,以便供其他方法更方便地获取该信息的场景。每个线程获取到的信息可能都是不一样的,前面执行的方法保存了信息后,后续方法可以通过ThreadLocal 直接获取到,避免了传参,类似于全局变量的概念。
ThreadLocal与Synchonized的区别
ThreadLocal<T>其实是与线程绑定的一个变量。ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
ThreadLocal 的 set 方法
为当前线程的 ThreadLocalMap 对象添加键值对,键为 ThreadLocal 对象,值为设置的 value 对象。
public void set(T value) {
// 获取当前线程
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
// 已经调用过set或get方法时,往线程的map中添加新的值,键为当前ThreadLocal对象
if (map != null)
map.set(this, value);
// 没有调用过set或get方法时,为当前线程创建一个ThreadLocalMap
// 并添加第一个值,键为当前ThreadLocal对象
else
createMap(t, value);
}
// 返回Thread对象t的ThreadLocalMap属性,初始为null
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocal 的 get 方法
线程中调用 get 方法时,如果之前用 set 方法为这个 ThreadLocal 键添加过值,那么就返回设置过的值,否则就返回默认初始值 null。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
// 已经调用过set或get方法时,对象值已经设置过,就返回上一次的值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// 没有调用过set或get方法时,对象值没有设置过,就自动设置初始值,并返回初始值
return setInitialValue();
}
private T setInitialValue() {
// 获得初始值
T value = initialValue();
// set方法
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
ThreadLocal 的 initialValue
通常可以在新建 ThreadLocal 对象时,采用匿名内部类的方式重写该方法,来设置共享的初始值。详细见参考资料1。
// 这是一个可重写的方法,用来设置ThreadLocal对象在所有线程中的初始值
protected T initialValue() {
return null;
}
那为啥ThreadLocal依然内存泄露的危险?
value值会存在一条强引用链,当线程存活时,Thread->Thread.threadlocals->ThreadLocalMap->value。
所以只要线程结束了,那么对应的threadLocalMap一定会被回收,不存在内存泄露的风险。
但是要是使用的是线程池,线程存活时间长,那么线程的threadLocalMap也不会被回收,那么就一直会存在一条对value值的强引用,无法回收value,就会造成内存泄露。
在使用线程池和ThreadLocal时,如何避免内存泄露?
在每次使用完ThreadLocal时,便调用ThreadLocal的remove()方法,remove()方法会在map中删除整个entry。
在使用线程池的情况下,没有及时清理ThreadLocal,不仅是内存泄漏的问题,更严重的是可能导致业务逻辑出现问题。所以,使用ThreadLocal就跟加锁完要解锁一样,用完就清理。
ThreadLocal是Java中用于线程局部变量的类,确保每个线程拥有独立的副本,避免线程安全问题。与synchronized不同,ThreadLocal不用于同步,而是用于数据隔离。在多线程环境中,ThreadLocal通过为每个线程创建变量副本,实现线程间的独立存储。然而,如果不正确使用,特别是在线程池中,可能会导致内存泄露。为防止内存泄露,应在使用完毕后调用ThreadLocal的remove方法。了解ThreadLocal的工作原理和管理策略对于优化并发程序至关重要。
10万+

被折叠的 条评论
为什么被折叠?



