ThreadLocal的简单使用和学习

ThreadLocal是一种Java类,用于在每个线程中创建独立的变量副本,实现线程隔离、上下文传递和减少同步。文章详细介绍了ThreadLocal的作用、使用场景以及注意事项,包括内存管理与不适用情况。
摘要由CSDN通过智能技术生成

ThreadLocal是什么

ThreadLocal,即线程本地变量。如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际是在操作自己本地内存里面的变量,从而起到线程隔离的作用,避免了并发场景下的线程安全问题。

ThreadLocal 的主要作用如下:

  1. 线程隔离ThreadLocal 可以实现线程间的数据隔离。每个线程可以独立地使用和修改自己的线程局部变量,而不会与其他线程的变量产生冲突。这对于多线程环境下的共享数据访问和线程安全性非常有用。
  2. 线程上下文传递ThreadLocal 可以用于在线程之间传递上下文信息。例如,在一个使用线程池的应用中,可以通过 ThreadLocal 在任务提交和执行之间传递一些上下文信息,而不需要显式地将上下文作为参数传递。
  3. 减少同步开销:使用 ThreadLocal 可以减少线程间进行同步的需求。由于每个线程都拥有自己的变量副本,不需要进行额外的同步操作,因此可以减少锁的竞争和同步开销,提高并发性能。
  4. 线程封闭性ThreadLocal 可以实现数据的线程封闭,即将某些数据限定在特定的线程中使用。这对于一些需要临时保存线程私有数据的场景非常有用,避免了数据被其他线程访问和修改的风险。

为什么使用ThreadLocal

并发场景下,会存在多个线程同时修改一个共享变量的场景。这就可能会出现线性安全问题

为了解决线性安全问题,可以用加锁的方式,比如使用synchronized 或者Lock。但是加锁的方式,可能会导致系统变慢。加锁示意图如下:

image-20231025192808721

还有另外一种方案,就是使用空间换时间的方式,即使用ThreadLocal。使用ThreadLocal类访问共享变量时,会在每个线程的本地,都保存一份共享变量的拷贝副本。多线程对共享变量修改时,实际上操作的是这个变量副本,从而保证线性安全。

image-20231025192821335

ThreadLocal使用案例

  1. 声明一个 ThreadLocal 类型的变量:

    private static ThreadLocal<T> threadLocal = new ThreadLocal<>();
    

    其中 T 是存储在 ThreadLocal 中的值的类型。

  2. 使用 ThreadLocal 类的 set() 方法设置值:

    threadLocal.set(value);
    

    这将把 value 存储在当前线程的 ThreadLocal 实例中。

  3. 使用 ThreadLocal 类的 get() 方法获取值:

    T value = threadLocal.get();
    

    这将返回当前线程的 ThreadLocal 实例中存储的值。

  4. 使用 ThreadLocal 类的 remove() 方法清除值(可选):

    threadLocal.remove();
    

    这将从当前线程的 ThreadLocal 实例中移除值。

  5. 最后,在不再需要 ThreadLocal 对象时,应调用 remove() 方法来清理资源:

    threadLocal.remove();
    

    这样可以避免潜在的内存泄漏问题。

需要注意的是,ThreadLocalset()get() 方法都是针对当前线程的操作。因此,在使用 ThreadLocal 时,应确保在同一线程范围内使用相同的 ThreadLocal 对象。这样才能保证在同一线程中的多个方法或代码段中共享同一个 ThreadLocal 实例。

此外,可以为 ThreadLocal 提供初始值和默认值。例如,可以使用 ThreadLocal 的构造函数或 initialValue() 方法来设置初始值:

private static ThreadLocal<T> threadLocal = new ThreadLocal<T>() {
    @Override
    protected T initialValue() {
        return initialValue;
    }
};

或者,可以在声明 ThreadLocal 变量时使用 lambada 表达式提供默认值:

private static ThreadLocal<T> threadLocal = ThreadLocal.withInitial(() -> defaultValue);

下面是一个简单的案例

public class ThreadLocalExample {
    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        // 在主线程中设置线程局部变量
        threadLocal.set("Main Thread");
        // 创建并启动子线程
        Thread childThread = new Thread(() -> {
            // 在子线程中获取并修改线程局部变量
            String value = threadLocal.get();
            System.out.println("子线程值: " + value);
            threadLocal.set("Child Thread");
            // 获取修改后的线程局部变量
            String modifiedValue = threadLocal.get();
            System.out.println("子线程值:" + modifiedValue);
            // 清理线程局部变量
            threadLocal.remove();
        });
        childThread.start();
        // 在主线程中获取线程局部变量
        String mainThreadValue = threadLocal.get();
        System.out.println("主线程值:" + mainThreadValue);
        // 清理线程局部变量
        threadLocal.remove();
    }
}

// 运行结果为
主线程值:Main Thread
子线程值: null
子线程修改后的值:Child Thread

ThreadLocal的应用场景和使用注意点

ThreadLocal很重要一个注意点,就是使用完,要手动调用remove()

ThreadLocal的应用场景主要有以下这几种:

  • 使用日期工具类,当用到SimpleDateFormat,使用ThreadLocal保证线性安全
  • 全局存储用户信息(用户信息存入ThreadLocal,那么当前线程在任何地方需要时,都可以使用)
  • 保证同一个线程,获取的数据库连接Connection是同一个,使用ThreadLocal来解决线程安全的问题
  • 使用MDC保存日志信息。

不适用场景:

  • ThreadLocal在高并发场景下可能存在性能问题。当多个线程同时修改ThreadLocal的值时,需要进行加锁操作,可能导致线程竞争和性能下降。

  • ThreadLocal的作用范围仅限于当前线程。如果需要在不同的线程之间传递数据ThreadLocal将无法起到作用

  • ThreadLocal在使用过程中需要特别注意内存泄漏问题。如果没有及时清除ThreadLocal的值或者线程一直处于活跃状态,可能导致 ThreadLocal对象无法被垃圾回收,进而造成内存泄漏。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值