ThreadLocal剖析

什么是ThreadLocal变量

ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。这里有几点需要注意:

  • 因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来。
  • 既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题。
    ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

四种引用

软引用

SoftRerence <byte[]> m = new Reference<>(new byte[1024*1024]);

在这里插入图片描述

当内存不够时,软引用的对象会回收。
缓存中用的比较多。

弱引用WeakReference(ThreadLocal)

在这里插入图片描述

只要垃圾回收就回收

虚引用(管理堆外内存)


PhantomReference<M> phantomReference = new PhantomReferecne<>(new M(),QUEUE);

作用:管理堆外内存
在这里插入图片描述

原理

在ThreadLocal内有ThreadLocalMap·内部类,一切操作都是围绕这个内部类展开的

  • ThreeadLocalMap的Key为 虚引用,key为threadLocal自身
    因为一个线程内可以存在多个 ThreadLocal 对象,所以其实是 ThreadLocal 内部维护了一个 Map ,这个 Map 不是直接使用的 HashMap ,而是 ThreadLocal 实现的一个叫做 ThreadLocalMap 的静态内部类。
    在Thread这个类中维持了一个ThreadLocalMap对象, ThreadLocal.ThreadLocalMap threadLocals = null;,在这个对象中就可以存储对象,每个Thread线程都有自己的一个ThreadLocalMap,然后 这个ThreadLocalMap的key就是每个ThreadLocal

    /**
     * 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.
     *     如果我们使用线程池,那就意味着当前线程未必会退出
     *              (比如固定大小的线程池,线程总是存在)。如果这样,将一些大大的对象设置到
     *              ThreadLocal中(它实际保存在线程持有的ThreradLocalMap中),可能会使得系统出现内存泄露的问题
     *
     */
    public void set(T value) {
        //获取当前想线程
        Thread t = Thread.currentThread();


        //拿到当前线程的ThreadLocalMap
        //这个值保存了当前自己所在线程的所有“局部变量”
        //每个线程各自的ThreadLocalMap
        //ThreadLocalMap可以理解为Map,它是定义在Thread内部的成员
        // 设置到ThreadLocal中的数据,也正是写入了threadLocals这个Map
        //其中key是ThreadLocal当前对象,value就是我们需要的值
        //threadLocalas本身就是保存了当前自己所在线程的所有局部变量。
        ThreadLocalMap map = getMap(t);


        //每个线程都有一个ThreadLocalMap
        // 每个线程存在ThreadLocalMap,在这个线程的threadLocalMap中可能存在多个threadLocal(线程的局部变量)
                //     threadLocal   --------------> 值  (局部变量)
                //      threadLoca2 -------------->值

        if (map != null)
            map.set(this, value);//key为当前ThreadLocal对象,value使我们需要的值
        else
            createMap(t, value);
    }

 public T get() {
        Thread t = Thread.currentThread();

        //获取当前线程的ThreadLocalMap对象,
        //通过key
        ThreadLocalMap map = getMap(t);

        if (map != null) {

            //将自己作为key取得内部的实际数据
            //ThreadLocal作为key
            ThreadLocalMap.Entry e = map.getEntry(this);

            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }


内存泄露问题


 /**
     * ThreadLocalMap is a customized hash map suitable only for
     * maintaining thread local values.
     *
     * No operations are exported outside of the ThreadLocal class.
     *
     * The class is package private to allow declaration of fields in class Thread.
     *
     * To help deal with
     * very large and long-lived usages, the hash table entries use
     * WeakReferences for keys.
     *为了应对大对象的生存,这里使用了若引用。
     *
     * However, since reference queues are not
     * used, stale entries are guaranteed to be removed only when
     * the table starts running out of space.
     */
    static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).
         *这个哈希映射中的条目扩展了WeakReference,
         * 使用它的主ref字段作为键(它总是aThreadLocal对象)。


          Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         * 注意,空键(即entry.get())表示键不再被引用,因此
         可以从表中删除条目。
         ThreadLocalMap的实现使用了弱引用 ,若引用是比强引用弱的多的引用,
         Java虚拟机在垃圾回收时,如果发现弱引用就会立即回收。
          如果对于ThreadLocal的变量,我们也手动将其设置为null,比如t1=null
         那么这个ThreadLocal所对应的局部变量是有可能被回收的(这里是因为需饮用)
         ThreadLocalMap内部由一系列Entry构成,每一个Entry都是WeakReference<ThreadLocal<?>>
         */
        //出于内存泄露的考虑将其设置为若引用
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;


            /**
             *
             * 虽然这里使用ThreadLocal作为 Map的key,但是实际上,
             * 它并不真的持有ThreadLocal的引用。而当ThreadLocal的外部引用被回收时,ThreadLocal中的key就会变成null
             * @param k
             * @param v
             */
            Entry(ThreadLocal<?> k, Object v) {
                //k是ThreadLocal实例,作为若引用(super(k)就是调用了WeakReference的构造函数)
                //当ThreadLocal的外部强引用被回收时,ThreadLocalMap中的key就会变成null
                //当系统进行ThreadLocalMap清理时
                // (比如将新的变量加入表中,就会自动进行一次清理)
                super(k);//key为若引用,key是Map的key
                //

                value = v;//v是
            }
        }

在这里插入图片描述
在这里插入图片描述

使用场景

  • 每个线程需要有自己单独的实例
  • 实例需要在多个方法中共享,但不希望被多线程共享
  • 存储用户session
  • 存储数据库连接
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值