ThreadLocal提供了线程本地变量,它可以保证访问到的变量属于当前线程,每个线程都保存有一个变量副本,每个线程的变量都不同。ThreadLocal相当于提供了一种线程隔离,将变量与线程相绑定。

Threadloca适用于在多线程的情况下,可以实现传递数据,实现线程隔离。
ThreadLocal提供给我们每个线程缓存局部变量

Threadlocal_基本API

  • New Threadlocal();---创建Threadlocal
  • set设置当前线程绑定的局部变量
  • get获取当前线程绑定的局部变量
  • remove(移除当前线程绑定的变量

哪些地方有使用Threadlocal

  • Spring事务模板类
  • 获取httprequest
  • Aop
  1. 设计模式 模板方法
  2. SpringMvc 获取httprequest对象 将httprequest缓存到当前线程中

单线程:Javaweb项目SpringMVC
tomcat接受请求
servlet框架处理请求
SpringMVC servlet做了一层封装----Aop拦截请求

----控制器层 Threadlocal获取到该httprequest

最终都是同一个线程在处理(中间经历了n多个不同的方法)

Threadlocal 与Synchronized区别

Synchronized 与 Threadlocal 都可以实现多线程访问,保证线程安全的问题。

A. Synchronized采用当多个线程竞争到同一个资源的时候,最终只能够有一个线程访问,采用****时间换空间** 的方式,保证线程安全问题
B.Threadlocal在每个线程中都自己独立的局部变量, **空间换时间** ,相互之间都是隔离。相比来说Threadlocal效率比 Synchronized效率更高。**

Threadlocal底层实现原理

1.在每个线程中都有自己独立的ThreadLocalMap对象,中 Entry对象。

2.如果当前线程对应的的ThreadLocalMap对象为空的情况下,则创建该ThreadLocalMap对象,并且赋值键值对。

Key为当前new ThreadLocal对象,value就是为object变量值。

在set方法下,会先获取当前线程,再创建一个ThreadLocalMap

为什么线程缓存的是ThreadlocalMap对象

  • ThreadlocalMap 可以存放n多个不同的ThreadLocal对象
  • 每个ThreadLocal对象只能缓存一个变量值
  • ThreadLocalMap <ThreadLocal对象,value> threadLocalMap

强,软,弱,虚引用区别

强引用

在 Java 中最常见的就是强引用, 把一个对象赋给一个引用变量,这个引用变量就是一个强引 用。当一个对象被强引用变量引用时 ,它处于可达状态,它是不可能被垃圾回收机制回收的,即 使该对象以后永远都不会被用到 JVM 也不会回收。 因此强引用是造成 Java 内存泄漏的主要原因之一

//定义一个强引用
        Object o1 = new Object();
        //o2引用赋值,不会再创建一个新的对象
        Object o2 = o1;
        o1 = null;
        System.gc();
        System.out.println(o1);
        System.out.println(o2);

软引用

软引用需要用 SoftReference 类来实现 ,对于只有软引用的对象来说,当系统内存足够时它 不会被回收,当系统内存空间不足时它会被回收。软引用通常用在对内存敏感的程序中。

o1 = new Object();
        SoftReference<Object> softReference = new SoftReference<>(o1);
        System.out.println("o1:"+o1);
        System.out.println("软引用对象"+softReference.get());
        o1 = null;
        try {
            byte[] bytes = new byte[30*1024*1024];
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            System.out.println("o1:"+o1);
            System.out.println("软引用对象"+softReference.get());
        }

弱引用

弱引用需要用 WeakReference 类来实现,它比软引用的生存期更短,对于只有弱引用的对象 来说,只要垃圾回收机制一运行,不管 JVM 的内存空间是否足够,总会回收该对象占用的内存。

o1 = new Object();
        WeakReference<Object> weakReference = new WeakReference<>(o1);
        System.out.println("o1:"+o1);
        System.out.println("弱引用对象"+weakReference.get());
        o1 = null;
        System.gc();
        System.out.println("o1:"+o1);
        System.out.println("弱引用对象"+weakReference.get());

虚引用

虚引用需要 PhantomReference 类来实现,它不能单独使用,必须和引用队列联合使用。 虚 引用的主要作用是跟踪对象被垃圾回收的状态。

形同虚设,研究应用不大

ThreadLocal为何引发内存泄漏问题

因为每个线程中都有自己独立的ThreadLocalMap对象,key为 ThreadLocal,value是为变量值。

Key为 ThreadLocal 作为Entry对象的key,是弱引用,当ThreadLocal 指向null 的时候,Entry对象中的key变为null,该对象一直无法被垃圾收集机制回收,一直占用到了系统内存,有可能会发生内存泄漏的问题。

public static void main(String[] args) {
        ThreadLocal<String> threadLocal = new ThreadLocal<>();
        threadLocal.set("hello");
        System.out.println(threadLocal.get());
        threadLocal=null;
        Thread thread=Thread.currentThread();
        System.out.println(thread);

    }

值与引用依然存在

ThreadLocalMap的key依然会指向堆内存中的值,虽然主线程中引用断开

如何防御ThreadLocal内存泄漏问题

什么是内存泄漏

内存泄漏表示就是我们程序申请了内存,但是该内存一直无法释放。

什么是内存溢出(OOM)

申请内存时,发现申请内存不足,就会报错内存溢出的问题。

如何解决ThreadLocal内存泄漏

  1. 可以自己调用remove方法将不要的数据移除避免内存泄漏的问题

先将ThreadLocalMap中的Entry<K,V>干掉

将主线程与堆内存之间的引用(指针)断开,gc便可实现垃圾回收

删除成功

  1. 每次在做set方法的时候会清除之前key为 null

  1. ThreadLocal对象为弱引用

Threadlocal采用弱引用而不是强引用

如果key是为强引用

当我们现在将ThreadLocal的引用指向为null,但是每个线程中有自己独立ThreadLocalMap还一直在继续持有该对象,但是我们ThreadLocal 对象不会被回收,就会发生 ThreadLocal内存泄漏的问题。

如果key是为弱引用

当我们现在将ThreadLocal 的引用指向为null,Entry中的key指向为null,但是

下次调用set方法的时候,会根据判断如果key空的情况下,直接删除,避免了Entry发生内存泄漏的问题。

不管是用强引用还是弱引用都是会发生内存泄漏的问题。弱引用中不会发生ThreadLocal内存泄漏的问题。

但是最终根本的原因Threadlocal 内存泄漏的问题,产生于ThreadLocalMap 与

我们当前线程的生命周期一样,如果没有手动的删除的情况下,就有可能会发生内存泄漏的问题

(强制)在代码逻辑中使用完ThreadLocal,都要调用remove方法,及时清理。(推荐)尽量不要使用全局的ThreadLocal。

最后修改:2022 年 10 月 06 日
如果觉得我的文章对你有用,请随意赞赏