谈谈 ThreadLocal 的用途

前言

ThreadLocal 提供了线程的局部变量,每个线程在对局部变量进行操作时,不会与其他线程的局部变量发生冲突,从而实现线程的数据隔离。那么,在了解了 ThreadLocal 的基本原理后,请大家思考一个问题,ThreadLocal 到底能做些什么呢?

如果大家对 ThreadLocal 的实现原理还不是太清楚的话,建议可以先看看我的上一篇博客 弄懂ThreadLocal,看这一篇就够了

用途

ThreadLocal 能让我们持有一个只有当前线程可见的变量,这有什么用呢?

管理 Connection

在与数据库进行交互时,频繁创建和关闭 Connection 是非常耗费资源的,故我们需要创建数据库连接池。那问题来了,我们应该如何管理数据库连接池里面的 Connection 呢?交给 ThreadLocal 来管理还是挺方便的,为啥?因为 ThreadLocal 能够保证当前线程的操作都是用同一个 Connection,从而保证了事务。

public class DBUtil {
    // 数据库连接池
    private static BasicDataSource source;

    // 管理不同线程的连接
    private static ThreadLocal<Connection> local;


    static {
        source = new BasicDataSource();
        local = new ThreadLocal<>();
    }

	// 获取 Connection
    public static Connection getConnection() {
        Connection connection = source.getConnection();

        // 将 Connection 放置到 ThreadLocal 里
        local.set(connection);
        return connection;
    }

    // 关闭 Connection
    public static void closeConnection() {
        // 从线程中获取 Connection
        Connection connection = local.get();

        if (connection != null) {
            // 将该连接归还给连接池
            connection.close();
            local.remove();
        }
    }

}
避免参数传递

在业务代码中,我们可能经常需要使用一些存放信息的类,每次用到我们都需要通过方法参数进行传递,非常麻烦,那么我是否可以使用一个工具来管理这些信息呢?没错,ThreadLocal 可以做到,它可以管理我们的各种存放信息类,在开始时我们可以把所有的信息通通放置到 ThreadLocal 里面,然后当我们需要使用时,不用再传递一个参数进去了,去 ThreadLocal 获取一下就好了。

应用:Spring 实现事务隔离级别

在 Spring 中,采用了 Threadlocal 来保证单个线程中的数据库操作使用的是同一个数据库连接,同时,采用这种方式可以使业务层使用事务时不需要感知并管理 connection 对象,通过传播级别,巧妙地管理多个事务配置之间的切换,挂起和恢复。

应用:SimpleDataFormat 多线程下存在的问题

在多线程环境下,使用 SimpleDataFormat 存在问题,可能会导致解析的时间不对。其实解决这个问题倒也不难,每一个线程创建一个 SimpleDataFormat 就好了嘛,但是这样效率就低了呀。

其实我们可以利用线程池,然后使用 ThreadLocal 包装 SimpleDataFormat,从而让每个线程有一个 SimpleDataFormat 的副本,在解决线程安全问题的同时也能提高性能。

内存泄露

我们知道,ThreadLocal 在保存时会将自己作为 Key 存放在 ThreadLocalMap 中,而且这个 key 是一个弱引用。一个只有弱引用的对象,无论内存空间是否足够,在垃圾回收时都会回收其内存。

显然,当 ThreadLocal 没有外部强引用时,发生 GC 时会被回收,如果创建 ThreadLocal 的线程一直持续运行,那么 value 就有可能一直得不到回收,发生内存泄露。例如线程池里面的线程,线程都是复用的,在之前的线程实例处理完之后,出于复用的目的,线程依然存活,所以,ThreadLocal 设定的 value 值被持有,导致内存泄露。正常来说,线程执行完后应该会把 ThreadLocalMap 清空的,可惜现在线程被复用了。

那怎么解决呢?其实,在代码的最后调用 remove 就好了,remove 会找到对应的值全部置空,那么在下次垃圾回收时会把它们给回收掉。

为什么 ThreadLocal 要使用 static 修饰

一个 ThreadLocal 实例对应当前线程中的一个 value,如果我们把 ThreadLocal 声明为某类的一个实例变量,那么每创建该类的一个实例都会导致一个 ThreadLocal 被创建,如果我们多次创建该类,那么该类的 ThreadLocal 对象就不在一个线程内共享了,这即使不引发错误,也会因为重复创建对象导致浪费。

另外,当我们使用 static 修饰 ThreadLocal 时,会延长 ThreadLocal 的生命周期,这会导致 ThreadMap 的 key 在线程生命期内始终有值,使 value 得不到释放,导致内存泄漏。

总结

对 ThreadLocal 一言概之,它的设计并不是为了解决并发或者共享变量的问题,只是为了能在当前线程中有属于自己的变量罢了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值