ThreadLocal

目录

一、什么是ThreadLocal?

二、ThreadLocal如何使用?

三、ThreadLocal内存泄漏问题

1、什么是内存泄漏

2、强引用与弱引用

3、GC回收机制

4、ThreadLocal内存泄漏问题

5、ThreadLocal正确的使用方法


一、什么是ThreadLocal?

    ThreadLocal翻译过来就是【线程本地 / 线程本地化 / 本地线程变量】,也就是说,ThreadLocal中填充的是当前线程的变量,该变量对其他线程而言,是封闭且隔离的,ThreadLocal为变量再每个线程中创建一个副本,这样每个线程都可以访问自己内部的副本变量。ThreadLocal是线程本地存储,再每个线程中都创建了一个ThreadLocalMap对象,每个线程可以访问自己内部ThreadLocalMap对象内的value。

二、ThreadLocal如何使用?

以下为示例:

public static void main(String[] args) {

        ThreadLocal<String> threadLocal = new ThreadLocal<>();

        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread();
            thread.start();
            threadLocal.set(thread.getName());
            System.out.println("线程:" + thread.getName() + ",Local:" + threadLocal.get() + ":" + i);
            threadLocal.remove();
        }
    }




输出结果:
线程:Thread-0,Local:Thread-0:0
线程:Thread-1,Local:Thread-1:1
线程:Thread-2,Local:Thread-2:2
线程:Thread-3,Local:Thread-3:3
线程:Thread-4,Local:Thread-4:4
线程:Thread-5,Local:Thread-5:5
线程:Thread-6,Local:Thread-6:6
线程:Thread-7,Local:Thread-7:7
线程:Thread-8,Local:Thread-8:8
线程:Thread-9,Local:Thread-9:9


代码解析:
1、这段代码创建了一个ThreadLocal对象,并在一个for循环中启动了10个线程。
2、ThreadLocal是一个Java线程范围内的变量。
3、每个线程都拥有自己独立的ThreadLocal变量副本,可以在不同的线程中存储不同的值。
4、在这段代码中,每个线程都通过调用threadLocal.set()方法将当前线程的名称存储在ThreadLocal对象中。
5、然后通过调用threadLocal.get()方法来获取ThreadLocal中存储的值,并将其和线程名称和循环变量i一起打印出来。
6、最后,调用threadLocal.remove()方法来清除ThreadLocal中的值,以确保下一个线程使用的是一个干净的ThreadLocal对象,防止内存溢出问题。

从结果中,我们可以看出,每一个线程都有自己的Local值,这就是ThreadLocal的基础使用方法。

三、ThreadLocal内存泄漏问题

1、什么是内存泄漏

广义通俗的来说,内存泄漏就是:不再会被使用的对象或者变量占用的内存不能被回收,就是内存泄漏。

2、强引用与弱引用

    强引用,使用最普遍的引用,一个对象具有强引用,不会被垃圾回收器回收。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError异常 / 错误,使程序异常终止,也不回收这种对象。如果想取消强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样可以使JVM在合适的时间就会回收该对象。

    弱引用,JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。可以在缓存中使用弱引用。

3、GC回收机制

垃圾回收机制,JVM找到需要回收的对象有以下两种方式:

  • 引用计数法:每个对象有一个引用技术属性,新增一个引用时计数加1,引用释放时,计数减1,计数为0时可以回收;
  • 可达性分析法:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象。

4、ThreadLocal内存泄漏问题

    static class ThreadLocalMap {
        static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;

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

ThreadLocal的实现原理,每一个Thread维护一个ThreadLocalMap,key为使用弱引用的ThreadLocal实例,value为线程变量的副本。这些对象之间的引用关系如下:

从上图中可以看出,hreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal不存在外部强引用时,key(ThreaddLocal)势必会被GC回收,这样就回导致ThreadLocalMap 中Key为null,而value还存在着强引用,只有Thread线程退出以后,value的强引用链条才会断掉。但是如果,当前线程在迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreadLocalMap -> Entry -> value,永远无法回收,造成内存泄漏。由于Thread中包含变量ThreadLocalMap,因此ThreadLocalMap与Thread的生命周期是一样长,如果都没有手动删除对应key,都会导致内存泄漏。但是使用弱引用可以多一层保障,弱引用ThreadLocadl不会内存泄漏,对应的value在下一次ThreadLocalMap调用set 、get、remove的时候会被清除。因此,导致内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除过key就会导致内存泄漏,而不是因为弱引用。

5、ThreadLocal正确的使用方法

  • 每次使用完ThreadLocal都调用remove()方法清除数据
  • 将ThreadLocal变量定义成private static,这样就一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhicheng..

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值