ThreadLocal内存泄漏&数据脏读问题

ThreadLocal是Java中的一个类,用于解决多线程环境下的并发问题。以下是对ThreadLocal的详细解释:

定义:
ThreadLocal,即线程局部变量,是Java提供的一种线程隔离的变量存储机制。每个线程都会有一个独立的ThreadLocal变量副本,这些副本之间互不干扰,从而实现线程间的数据隔离。
原理:
ThreadLocal内部维护了一个名为ThreadLocalMap的静态内部类,该Map的键是ThreadLocal对象本身(实际上是ThreadLocal的一个弱引用),值是线程变量的副本。每个线程都有一个自己的ThreadLocalMap实例,用于存储该线程独有的变量副本。因此,当线程访问ThreadLocal变量时,会通过自身的ThreadLocalMap获取对应的变量副本,从而保证了线程间的数据隔离。

  • ThreadLocal的set方法
    public void set(T value) {
   
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
   
            map.set(this, value);
        } else {
   
            createMap(t, value);
        }
    }

简单来说,每个Thread线程都维护了一个ThreadLocalMap,当使用ThreadLocal对象存值时,会先根据当前线程找到ThreadLocalMap,这个ThreadLocalMap 的key是ThreadLocal对象,值就是ThreadLocal对象存储的值。因为一个线程运行期间会创建很多的ThreadLocal对象,所以这里是个Map结构。

  • ThreadLocalMap
        static class Entry extends WeakReference<ThreadLocal<?>> {
   
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
   
                super(k);
                value = v;
            }
        }

在ThreadLocalMap中key也就是ThreadLocal对象是弱引用的,当一个线程运行完成,如果栈内存或堆内存中没有强引用关联,则一定会被GC掉,但是value不会回收,value是和线程生命周期绑定的,线程不销毁,value也不会销毁。但是像Tomcat或者在业务系统中自定义的线程池都是会复用线程的,如果一直在创建ThreadLocal,则会导致内存泄漏,进而引发内存溢出。

  • 脏读代码示例

一般的,ThreadLocal会和当前运行线程绑定,如果运行线程不销毁,且没有清理ThreadLocal中的值,则会产生脏读。

例如Tomcat每次请求来都会开一个线程处理,但是线程是被线程池管理的,执行完一次请求之后,线程会被复用,如果一个线程ThreadLocal没有及时清理,则下一个请求可能会由于业务逻辑判断错误读取到其他线程的值,我使用CompletableFuture对象来模拟实现一下。

  • 定义一个数据值存储和查询对象
public class DataHandlerSafe<T> {
   

    private ThreadLocal<T> data = new ThreadLocal<>();

    public void setData(T t){
   
        this.data.set(t);
    }

    public T 
ThreadLocal 内存泄露问题是指当使用ThreadLocal类时,如果没有正确地进行清理和处理,就有可能导致内存泄露的情况发生。这是因为ThreadLocal对象的生命周期与线程的生命周期相对独立,当线程结束时,ThreadLocal对象没有被垃圾回收,且其中存储的数据也无法被访问,从而导致内存泄露。 具体来说,ThreadLocal类通过操作ThreadLocalMap来存储每个线程的数据。当一个线程结束时,如果没有正确地清理ThreadLocal对象,那么ThreadLocalMap中与该线程相关的条目将无法被删除。这意味着,即使这些条目对应的线程不再活跃,它们却仍然占据着内存空间。 一种常见的导致ThreadLocal内存泄露的情况是在使用完ThreadLocal对象后未调用其remove方法进行清理操作。如果在一个长时间运行的线程中重复使用ThreadLocal对象,而不进行清理操作,就会导致ThreadLocalMap中的条目越来越多,从而造成内存泄露。 另外,当ThreadLocal对象被作为静态变量使用时,也容易出现内存泄露的问题。因为静态变量的生命周期很长,如果静态ThreadLocal对象没有被妥善处理,那么其中的数据也将无法被释放。 为了避免ThreadLocal内存泄露,应该养成良好的编程习惯,确保在使用完ThreadLocal对象后,及时调用其remove方法进行清理。另外,如果ThreadLocal对象被用作静态变量,也应该在不再使用时手动将其置为null,以便让垃圾回收器能够回收相关的内存空间。 参考资料: :可以发现问题ThreadLocal已经被清理掉了,代表现在已经没有方式去访问当前ThreadLocal存到Map里的value数据了 。 :ThreadLocal就相当于一个访问工具类,通过操作ThreadLocal对象的方法 来操作存储在当前线程内部的ThreadLocalMap里的值 。 :一篇文章我们来分析一个Java中ThreadLocal内存泄露的案例。分析问题的过程比结果更重要,理论结合实际才能彻底分析出内存泄漏的原因。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值