ThreadLocal

什么是ThreadLocal

ThreadLocal 是 Java 里一种特殊变量,它是一个线程级别变量,每个线程都有一个 ThreadLocal 就是每个线程都拥有了自己独立的一个变量,竞态条件被彻底消除了,在并发模式下是绝对安全的变量。用于线程内共享

 

使用demo

static   ThreadLocal threadLocal=new ThreadLocal();
    public static void main(String[] args) {
        //<1>set
        threadLocal.set("dd");
        test();
    }
    public static  void  test(){
        //线程内共享 打印dd
        System.out.println(threadLocal.get());
    }

ThreadLocal

<1>set

java.lang.ThreadLocal#set

    public void set(T value) {
        //获得当前线程对象
        Thread t = Thread.currentThread();
        //<3>获得t的threadLocals的成员变量 也就是ThreadLocalMap
        ThreadLocal.ThreadLocalMap map = getMap(t);
        if (map != null)
            //<4>ThreaLocal为key value为我们的值
            map.set(this, value);
        else
            //为t初始化一个ThreaLocalMap并设置值
            createMap(t, value);
    }

<3>getMap

java.lang.ThreadLocal#getMap

//返回ThreadLocal.ThreadLocalMap  
 ThreadLocal.ThreadLocalMap getMap(Thread t) {
        //获得Thread的成员变量threadLocals
        return t.threadLocals;
    }

从上面可以看出我们的数据都是存储在ThreadLocalMap,ThreadLocalMap就是一个普通的map通过ThreadLocal对象为key

<4>set

java.lang.ThreadLocal.ThreadLocalMap#set

 private void set(ThreadLocal key, Object value) {

        // We don't use a fast path as with get() because it is at
        // least as common to use set() to create new entries as
        // it is to replace existing ones, in which case, a fast
        // path would fail more often than not.
        //ThreaLocalMap内部通能数组来保存 通过Entry封装
        Entry[] tab = table;
        int len = tab.length;
        int i = key.threadLocalHashCode & (len-1);

        for (ThreadLocal.ThreadLocalMap.Entry e = tab[i];
             e != null;
             //threadLocalMap是通过线性探测法来解决hash碰撞,当出现hash碰撞查找数组的下一个位置
             e = tab[i = nextIndex(i, len)]) {
            ThreadLocal k = e.get();

            if (k == key) {
                e.value = value;
                return;
            }

            if (k == null) {
                replaceStaleEntry(key, value, i);
                return;
            }
        }
        //<5>通过内部内Entry 来存储 key为ThreadLocal value为我们set的值 并存储到数组的具体位置
        tab[i] = new  Entry(key, value); 
        int sz = ++size;
        if (!cleanSomeSlots(i, sz) && sz >= threshold)
            rehash();
    }

<5>Entry

 /**
     * ThreadLocalMap静态内部类
     * WeakReference 为弱引用,当弱引用引用的的对象没有被强引用时也会被回收
     * 这里可以发现 value是强引用
     * super(k) key为ThreadLocal 传给了父类 则是弱引用
     */
    static class Entry extends WeakReference> {
        /** The value associated with this ThreadLocal. */
        Object value;

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

ThreadLocal内存泄露

根据<5>处我们可以看到ThradLocal没有被强引用时就会被垃圾回收 即Entry Key会被回收,但是value还被Entry引用 ,Entry被ThreadLocalMap引用 ThreadLocalMap被Thread引用,所以在线程未被回收前对象会一直不会被回收,如果使用线程池技术 可能导致内存泄露

如:

public static void main(String[] args) {
        test();
    }
    public static  void  test(){
        ThreadLocal threadLocal=new ThreadLocal();
        threadLocal.set(new Student());;
    }

弱引用Entry的keyThreadLocal对象会被回收,但是Entry还持有value的引用 ThreadLocalMap持有Entry引用  Thread持有ThreadLocalMap引用 导致内存泄露,所以当使用完要手动调用remove方法

public static  void  test(){
        ThreadLocal threadLocal=new ThreadLocal();
        threadLocal.set("ddd");;
        threadLocal.remove();
    }

 

总结

1.ThreadLocal本质获取当前线程对象的成员变量ThreadLocalMap ThreadLocalMap通过ThreadLocal作为key存储相应的value value通过Entry封装

2.使用map的原因是因为一个线程可以定义多个ThreadLocal

你可能感兴趣的:(ThreadLocal)