ThreadLocal

ThreadLocal是Java中用于线程局部变量的类,确保每个线程拥有独立的副本,避免线程安全问题。与synchronized不同,ThreadLocal不用于同步,而是用于数据隔离。在多线程环境中,ThreadLocal通过为每个线程创建变量副本,实现线程间的独立存储。然而,如果不正确使用,特别是在线程池中,可能会导致内存泄露。为防止内存泄露,应在使用完毕后调用ThreadLocal的remove方法。了解ThreadLocal的工作原理和管理策略对于优化并发程序至关重要。

 

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就跟加锁完要解锁一样,用完就清理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值