ThreadLocal不懂的就过来看看吧

既然写了博客我就写的详细一些,尽量易懂一些(反正我也操作了,哈哈),以后慢慢的就不会了呦

来上才艺了哈:学习、认识三部曲:

1. ThreadLocal是什么
2. ThreadLocal的应用
3. ThreadLocal怎么实现的
4. ThreadLocal有没有问题(内存泄漏)
5. 融会贯通

那我们开始吧

ThreadLocal是什么

当程序每新启动一个线程(web应用上包括用户访问),ThreadLocal会依据你当前的线程给你保存一些信息,仅仅是和你当前线程相关。

ThreadLocal的应用

既然可以携带和你当前线程有关的信息,那是不是我可以把每个访问线程都放到里面,就方便存取了呢,哪些应用可以实现(之前接触的有放入用户信息的),做的最多的可能就是去操作数据,里面放的都connection信息,这样操作就相当于每个线程只建立一个连接。给你个示例代码自己先看看

public class DBUtil {
    // 数据库配置
    private static final String driver = "com.mysql.jdbc.Driver";
    private static final String url = "jdbc:mysql://localhost:3306/demo";
    private static final String username = "root";
    private static final String password = "root";

    // 定义一个用于放置数据库连接的局部线程变量(使每个线程都拥有自己的连接)
    private static ThreadLocal<Connection> connContainer = new ThreadLocal<Connection>();

    // 获取连接
    public static Connection getConnection() {
        Connection conn = connContainer.get();
        try {
            if (conn == null) {
                Class.forName(driver);
                conn = DriverManager.getConnection(url, username, password);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            connContainer.set(conn);
        }
        return conn;
    }

    // 关闭连接
    public static void closeConnection() {
        Connection conn = connContainer.get();
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            connContainer.remove();
        }
    }
}

ThreadLocal怎么实现的

既然已经知道获取的是当前线程相关的那就解析一下这个过程。先上类图:
在这里插入图片描述
应该先看 Thread 里面:

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

threadLocals 是一个map 没错就是它,它保存的是你的当前线程和你对应的value,对应的
ThreadLocalMap 是ThreadLocal的内部类。这个就说明了,我把它交给了ThreadLocal去管理。
那现在就得看它的初始化了,一开始它做了什么:

   /**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
     /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

当然了有些会重写它的初始化,在获取的时候,这样就可以直接赋值了。
看到了吧 threadLocals 的key 就是 当前的ThreadLocal。不行再看下set 和 get 哈

   /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

我已经尽力了。
简单的说就是:
Thread.threadLocals(map) ------>ThreadLocal.ThreadLocalMap(ThreadLocal为键,注意ThreadLocalMap是个静态class)------>set、get 调用初始化赋值

ThreadLocal有没有问题(内存泄漏)

Thread---->ThreadLocal.ThreadLocalMap
其实还是map中的内存泄漏问题。map中把theadlocal 变成null,GC之后在map中存储的弱引用对象的hashcode来作为key,那么它就被回收了,但是它的value一直就被放在堆里面,并且被当前的线程threadLocals引用,因为在线程池里的他可能一直都不释放,导致内存泄漏了。 所以导致内存泄漏的情况需要满足:

  1. 触发了垃圾回收
  2. 没有调用get、set、remove因为如果调用这个threadlocal 会发现键为空了,会重新赋值
  3. 线程一直运行
    所以解决方案:
    最简单有效的方法是使用后将其移除 记得用remove

过程大家可以参考这个我觉得还没他写的好
ThreadLocal理解及应用
我们来分析一下这个mat图:
这个视图呢,是分析存活的最大空间占用的,在每一列的头部可以正则搜索,我们搜索
在这里插入图片描述
这个是分析对象实例个数和占用的
在这里插入图片描述
这个工具还可以分析多个dump文件来进行对比分析
JVM故障分析及性能优化
然后搜索发现没有key的引用,只有value的空间占用很大。
验证了内存泄漏的问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadLocalJava中的一个线程局部变量工具类,它为每个线程提供了独立的变量副本。由于每个线程都有自己独立的副本,不同线程之间的访问不会相互影响,因此可以提高线程的安全性和并发性。 然而,虽然ThreadLocal能够提供线程安全性,但在某些情况下它也可能会导致内存泄漏。主要体现在以下两个方面: 1.没有及时清理资源:ThreadLocal使用完毕后,如果没有主动调用remove方法来清理其引用的对象,会导致该线程的ThreadLocalMap中的Entry对象无法被回收,而且这些对象所引用的值对象也无法被回收,从而可能导致内存泄漏。 2.长时间不回收的线程:如果使用ThreadLocal的线程生命周期较长,尤其是在应用服务器环境下,线程池中的线程容易存在长时间不被回收的情况。在这种情况下,如果ThreadLocal中引用的对象持有较大的资源,比如数据库连接、I/O流等,就会导致这些资源得不到释放,从而可能导致内存泄漏。 为了避免ThreadLocal的内存泄漏,我们可以采取以下措施: 1.使用完ThreadLocal后,手动调用其remove方法,清理引用的对象。最好将remove方法的调用放在finally块中,确保资源会被释放。 2.在长时间不使用ThreadLocal的线程中,及时将ThreadLocal实例的引用置为null,以便能够被垃圾回收。 3.尽量缩小ThreadLocal的作用范围,避免在整个应用程序中共享一个ThreadLocal实例,以减少内存泄漏的潜在风险。 综上所述,虽然ThreadLocal在提供线程安全性方面非常有用,但在使用过程中需要特别注意资源的清理和释放,以避免可能产生的内存泄漏问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值