【ThreadLocal总结】

为什么使用ThreadLocal

在并发编程中,多个线程同时访问和修改共享变量是一个常见的场景。这种情况下,可能会出现线程安全问题,即多个线程对共享变量的操作可能会相互干扰,导致数据不一致。

为了解决线程安全问题,一种常见的做法是使用锁机制,如synchronized关键字或Lock接口。然而,加锁的方式可能会带来性能上的损失,因为线程之间需要竞争锁,而且在等待锁的过程中会阻塞线程的执行。

另一种解决方案是使用ThreadLocal。ThreadLocal提供了一种空间换时间的方式来解决线程安全问题。它为每个线程创建了一个独立的存储空间,用于保存线程特有的数据。当多个线程访问同一个ThreadLocal变量时,实际上它们访问的是各自线程本地存储的副本,而不是共享变量本身。因此,每个线程都可以独立地修改自己的副本,而不会影响到其他线程。

使用ThreadLocal的好处在于它避免了线程之间的竞争和阻塞,提高了并发性能。同时,它也简化了编程模型,因为开发者不需要显式地使用锁来保护共享变量的访问。

需要注意的是,ThreadLocal并不适用于所有场景。它主要适用于每个线程需要独立保存自己的数据副本的情况。如果多个线程之间需要共享数据并进行协作,那么使用锁或其他同步机制可能更为合适。此外,在使用ThreadLocal时也需要注意内存泄漏和数据污染的问题,需要正确地管理和清理线程本地存储的数据。

ThreadLocal核心

ThreadLocal是java包下的工具类,它提供了一种线程级别的数据隔离机制。通过ThreadLocal,我们可以在每个线程中存储自己的数据副本,互不影响,避免了多线程编程中的共享数据问题。
核心特点:
核心特性
1.线程隔离:每个线程对 ThreadLocal 变量的修改对其他线程是不可见的。
2.无继承性:子线程不能访问父线程的 ThreadLocal 变量,除非子线程中有显式的设置或复制操作。
3.避免同步:由于每个线程都有自己的变量副本,因此不需要同步就可以保证线程安全。
常见方法
public T get():返回当前线程对应的变量的值。如果当前线程没有对应的值,则返回初始值或 null(如果未设置初始值)。
public void set(T value):设置当前线程对应的变量的值。
public void remove():删除当前线程对应的变量。
protected T initialValue():这是一个受保护的方法,用于设置变量的初始值。通常,你可以通过匿名内部类来覆盖这个方法。

ThreadLocal内部结构

在这里插入图片描述

可以看到,ThreadLocal对象是存储在每个Thread线程内部的ThreadLocalMap中的,并且在ThreadLocalMap中有一个Entry数组,Entry数组中的每一个元素都是一个Entry对象。

每个Entry对象中存储着一个ThreadLocal对象与其对应的value值,每个Entry对象在Entry数组中的位置是通过ThreadLocal对象的threadLocalHashCode计算出来的,以此来快速定位Entry对象在Entry数组中的位置。所以,在Thread中,可以存储多个ThreadLocal对象。

ThreadLocal内存泄漏

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

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

可以看到,Entry类继承了WeakReference类,WeakReference类的泛型是ThreadLocal,,说明ThreadLocalMap中的Entry数组对Entry对象的Key就是弱引用。所以,Entry对象中的Key可以被GC自动回收。当Thread被回收后
在这里插入图片描述
当Entry对象中的Key被GC自动回收后,对应的ThreadLocal被GC回收掉了,变成了null,但是ThreadLocal对应的value值依然被Entry引用,不能被GC自动回收。
此时,我们可以看到,Entry对象中的Key,也就是ThreadLocal对象可以被GC自动回收,但是对应的value还在被引用,所以,value是不能被GC自动回收的,这种情况下就会存在内存泄露的风险。

解决内存泄漏

  public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null) {
             m.remove(this);
         }
     }

remove方法会将ThreadLocal为null对应的value设置为null,同时会把对应的Entry对象也设置为null,并且会将所有ThreadLocal对应的value为null的Entry对象设置为null,这样就去除了强引用,便于后续的GC进行自动垃圾回收,也就避免了内存泄露的问题。
在这里插入图片描述
所以要求我们在平时的代码中做到用完就remove

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值