ThreadLocal原理用法详解ThreadLocal内存泄漏

ThreadLocal

https://github.com/CyC2018/CS-Notes/blob/master/notes/Java%20%E5%B9%B6%E5%8F%91.md#2-%E7%BA%BF%E7%A8%8B%E6%9C%AC%E5%9C%B0%E5%AD%98%E5%82%A8thread-local-storage

ThreadLocal,即线程变量

常用的3个方法:set()、get()、remove()。都是线程安全的。

一个ThreadLocal在一个线程中是共享的,在不同线程之间又是隔离的(每个线程都只能看到自己线程的值)

public class ThreadLocalExample1 {
    public static void main(String[] args) {
        ThreadLocal threadLocal1 = new ThreadLocal();
        ThreadLocal threadLocal2 = new ThreadLocal();
        Thread thread1 = new Thread(() -> {
            threadLocal1.set(1);
            //threadLocal1.set(11);会覆盖上面的set(1)
            threadLocal2.set(1);
        });
        Thread thread2 = new Thread(() -> {
            threadLocal1.set(2);
            threadLocal2.set(2);
        });
        thread1.start();
        thread2.start();
    }
}

ThreadLocal原理

在这里插入图片描述

每个 Thread 都有一个 ThreadLocal.ThreadLocalMap 对象。

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

set

ThreadLocal.ThreadLocalMap threadLocals = null;
...
public void set(T value) {
    Thread t = Thread.currentThread();
    //获取当前线程的ThreadLocalMap;
    ThreadLocalMap map = getMap(t);
    if (map != null)
    //this是调用set()方法的localthread对象;
        map.set(this, value);
    else
        createMap(t, value);
}
...
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

set()先获取当前线程的ThreadLocalMap,然后将调用set()方法的localthread对象作为key存储value,

这样的话,ThreadLocal threadLocal1 = new ThreadLocal(),线程a调用threadLocal1.set方法时,线程a会创建ThreadLocalMap对象,key是threadLocal1;
当线程调用threadLocal1.get时,线程a便会到自己的ThreadLocalMap中,根据key==threadLocal1获取value,这样便是线程安全的,每个线程都有自己的ThreadLocalMap

get

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();
}

get方法,先获取当前线程的ThreadLocalMap,然后根据将调用get()方法的localthread对象作为key获取value;

ThreadLocal内存泄漏

内存泄露:程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重;

ThreadLocalMap的key

static class ThreadLocalMap {

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

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

在这里插入图片描述

如上图,每一个Thread维护一个ThreadLocalMap,key弱引用,外部的ThreadLocalRef是key的强引用,一旦ThreadLocalRefnull,GC时key就会被回收,但是value还存在强引用,只有CurrentThreadRefnull,value才会被回收;

这些key为null的Entry的value就会一直存在一条强引用链

CurrentThreadRef -> CurrentThread -> ThreaLocalMap -> Entry -> value

public class ThreadLocalExample1 {
    public static void main(String[] args) {
        ThreadLocal threadLocal1 = new ThreadLocal();
        Thread thread1 = new Thread(() -> {
        while(true){
            threadLocal1.set(1);
        }
        });
        threadLocal1 = null;
        System.GC();//如果在这里发生GC,那么threadLocal1以及thread1的ThreaLocalMap的key会被回收,但是value不会被回收;
        thread1.start();
    }
}

https://blog.csdn.net/puppylpg/article/details/80433271
https://zhuanlan.zhihu.com/p/102571059


©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页