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的空间占用很大。
验证了内存泄漏的问题

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章