ThreadLocal原理分析

ThreadLocal是什么?

ThreadLocal就是指线程局部变量,就是指多个线程并发运行的时候,使用ThreadLocal装饰的变量在每个线程里都是单独使用的。

好处

由于每个线程拥有了自己的变量,所以消除了多线程情况下的竞争关系。

实现原理

要理解ThreadLocal的实现原理,那还是得从源码出发

    public void set(T value) {
        Thread t = Thread.currentThread(); // 获取当前线程
        ThreadLocalMap map = getMap(t);  // 获取一个map
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this); // 获取到一个值
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

由上面的代码可知,设置和获取值得时候都会获取到一个ThreadLocalMap,并且用ThreadLocal作为key,真正需要使用的值作为value

ThreadLocal.ThreadLocalMap threadLocals = null; // 当前线程的map

接下来还需要关注ThreadLocalMap对象的存储方式

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

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

从Entry可以很明细的知道,在map中的key是一个弱引用,当key在没有强引用的时候,gc的时候key就会被回收掉,也就是当ThreadLocal变量设置为null的时候就会被回收

为什么key设置为弱引用?

首先假设key不是弱引用,而是强引用,当ThreadLocal变量设置为null的时候,由于map中还存在强引用,那么ThreadLocal变量在线程没结束就永远不会被gc回收掉。那么这样就会出现内存泄漏的问题。

所以为了避免ThreadLocal设置为null的时候不会因为map引用而导致内存泄漏的问题,所以key就必须用弱引用。

ThreadLocal为什么会出现内存泄漏?

从源码分析可知道,value值是强引用的,当ThreadLocal变量设置为null的时候,key被回收了,但value还没被回收,而且由于key为null而导致value永远不可达,虽然ThreadLocalMap中的set(),get(),remove()这些方法引用的时候,会将key为null,value不为null的清除掉。但假设ThreadLocal变量设置为null的时候,长时间没调用过以上的3个方法,并且线程长时间存在,这时候就会导致value内存泄漏了。

怎么避免ThreadLocal的内存泄漏?

每次使用完ThreadLocal之后都调用一下remove()方法,清除数据,这样就可以避免内存泄漏问题了。

你可能感兴趣的:(java)