ThreadLocal原理

1、ThreadLocal定义

       ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。
       同一个ThreadLocal所包含的对象,在不同的Thread中有不同的副本。有以下两点需要注意:
       ●因为每个Thread内有自己的实例副本,且该副本只能由当前Thread使用。这也是ThreadLocal命名的由来。
       ●既然每个Thread有自己的实例副本,且其他Thread不可访问,那就不存在多线程间共享的问题。

2、实现原理

set()方法
       每一个线程都拥有一个ThreadLocalhostMap,如果没有,在第一次执行set()方法时创建一个ThreadLocalhostMap,也就是下面的createMap()方法,只需new一个ThreadLocalMap传给Thread的成员变量threadLocals;如果存在一个ThreadLocalhostMap,获得该Thread的threadLocals,并插入新的键值对(threadLocal和要set的value);

public void set(T value) {
		//找到当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        //如果map不为空,就插入这组键值对
        if (map != null)
            map.set(this, value);
        //如果map为空,就创建一个map
        else
            createMap(t, value);
    }
//获得线程的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
//Thread类持有ThreadLocalMap类型的成员变量threadLocals
ThreadLocal.ThreadLocalMap threadLocals = null;
//创建ThreadLocalMap
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

ThreadLocalMap的结构
       每个线程有自己的ThreadLocalMap,ThreadLocalMap的元素是Entry,是ThreadLocal和要set的value。ThreadLocalMap和HashMap有点类似,不过HashMap是数组+链表的数据结构,采用拉链法解决hash冲突;而ThreadLocalMap是一个Entry数组,采用开放定址法解决hash冲突。
在这里插入图片描述
ThreadLocalMap的源码

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

get()方法
       是根据ThreadLocal获取当前线程ThreadLocalMap下的Entry,进而获取Entry中的value值。

public T get() {
		//获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null) {
        	//获取当前ThreadLocal对于的Entry
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                //获取value
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

3、Entry中的key为什么是弱引用的

Entry源码

static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

       如下图所示,Entry中的key是弱引用的,那为什么不是强引用呢,如果是强引用,在线程里把ThreadLocal置为null,那就意味着从线程栈指向堆中的ThreadLocal实例的强引用将不存在,这样堆里的ThreadLocal实例就会被回收了吗,答案是否定的,因为它还有一条key对ThreadLocal的强引用,ThreadLocal将不会被回收,会导致内存泄露。如果是弱引用的话,ThreadLocal会被回收, ThreadLocalMap中使用这个 ThreadLocal 的 key 也会被清理掉。
在这里插入图片描述

4、内存泄露

       虽然Entry中的key为弱引用,但ThreadLocal仍会导致内存泄露,原因是value仍存在ThreadLocalMap对它的强引用,不会被回收。只有当前thread结束以后, current thread就不会存在栈中,强引用断开, Current Thread, Map, value将全部被GC回收。当线程的某个ThreadLocal使用完了的时候及时调用ThreadLocal的remove方法,防止内存泄露。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值