ThreadLocal详解

ThreadLocal详解

一、故事背景

最近在学习并发编程相关内容,这里给大家分享一下ThreadLocal如何使用以及不规范的使用ThreadLocal可能会造成的问题。

二、知识点主要构成

1、什么是ThreadLocal?

ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来。既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题。ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。

2、ThreadLocal的基本使用

java 复制代码
public class ThreadLocalDemo02 {
    private ThreadLocal<Person> threadLocal = new ThreadLocal<>();
    public Person getContent() {
        return threadLocal.get();
    }
    public void setContent(Person person) {
        threadLocal.set(person);
    }
    public static void main(String[] args) {
        ThreadLocalDemo02 threadLocalDemo02 = new ThreadLocalDemo02();
        Person person = new Person();
        person.setName("wmj");
        new Thread(() ->{
            threadLocalDemo02.setContent(person);
            System.out.println(threadLocalDemo02.getContent());
        }).start();

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{
            System.out.println(threadLocalDemo02.getContent());
        }).start();
    }
}

内存泄漏问题

在介绍ThreadLocal造成的内存泄漏问题之前先给大家介绍一下Java中的四种引用类型:

引用类型:

强引用:

最常见的普通对象引用,只要还有一个强引用指向一个对象,就能表明对象还活着,垃圾回收器就不会回收这种对象;

java 复制代码
public class Client {
    public static void main(String[] args) {
        M m = new M();
        m = null;
        System.gc();
    }
}

软引用

内存不足时则会触发gc清理软引用

java 复制代码
public class TestSoftReference {
    public static void main(String[] args) {
        //软引用
        SoftReference<byte[]> softReference = new SoftReference<>(new byte[1024 * 1024 * 10]);
        System.out.println(softReference.get());
        System.gc();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(softReference.get());

        byte[] bytes = new byte[1024 * 1024 * 12];
        System.out.println(softReference.get());
    }
}

弱引用

GC一旦发现里只具有弱引用的对象,不管当前内存空间是否足够,都会回收它的内存;

java 复制代码
public class WeakDemo {
    public static void main(String[] args) {
        WeakReference wr = new WeakReference(new Person());
        System.out.println(wr.get());
        System.gc();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(wr.get());
    }
}

虚引用

虚引用大多被用于引用销毁前的处理工作,本次暂不介绍关于虚引用的详细操作。

ThreadLocal内存泄漏原因

ThreadLocal提供了线程的局部变量,每个线程都可以通过set()和get()来对这个局部变量进行操作,但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离。

Thread为每个线程维护了ThreadLocalMap这么一个Map,而ThreadLocalMap的key是LocalThread对象本身,value则是要存储的对象。

而key被保存到了弱引用WeakReference对象中,ThreadLocal在没有外部强引用时,发生GC时会被回收(弱引用就是只要JVM垃圾回收器发现了它,就会将之回收),而这个Entry对象中的value就有可能一直得不到回收,那时就会发生内存泄露。

三、总结提升

threadlocl是作为当前线程中属性ThreadLocalMap集合中的某一个Entry的key值Entry(threadlocl,value),虽然不同的线程之间threadlocal这个key值是一样,但是不同的线程所拥有的ThreadLocalMap是独一无二的,也就是不同的线程间同一个ThreadLocal(key)对应存储的值(value)不一样,从而到达了线程间变量隔离的目的,但是在同一个线程中这个value变量地址是一样的。ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景。

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏哦。

相关推荐
郝YH是人间理想几秒前
OpenCV基础——傅里叶变换、角点检测
开发语言·图像处理·人工智能·python·opencv·计算机视觉
wisdom_zhe2 分钟前
Spring Boot 日志 配置 SLF4J 和 Logback
java·spring boot·logback
Tiger Z3 分钟前
R 语言科研绘图第 36 期 --- 饼状图-基础
开发语言·程序人生·r语言·贴图
揣晓丹13 分钟前
JAVA实战开源项目:校园失物招领系统(Vue+SpringBoot) 附源码
java·开发语言·vue.js·spring boot·开源
于过24 分钟前
Spring注解编程模型
java·后端
北随琛烬入25 分钟前
Spark(10)配置Hadoop集群-集群配置
java·hadoop·spark
顽疲30 分钟前
从零用java实现 小红书 springboot vue uniapp (11)集成AI聊天机器人
java·vue.js·spring boot·ai
霍徵琅37 分钟前
Groovy语言的物联网
开发语言·后端·golang
Yan-英杰1 小时前
DeepSeek-R1模型现已登录亚马逊云科技
java·大数据·人工智能·科技·机器学习·云计算·deepseek
TDengine (老段)1 小时前
TDengine 中的日志系统
java·大数据·数据库·物联网·时序数据库·tdengine·iotdb