ThreadLocal的一点理解记录

ThreadLocal对象的作用就是为每一个线程开辟出一份副本区域,用于存储当前线程的变量信息。

当它在存入对象信息的时候

 public void set(T value) {
        //获取当前线程变量信息
        Thread t = Thread.currentThread();
        //根据当前线程获取ThreadLocalMap对象,ThreadLocalMap是一个实现了类似Map集合功能的对象
        ThreadLocalMap map = getMap(t);
        //如果ThreadLocalMap存在,则把当前LocalThread对象和值存入
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

ThreadLocalMap内部存储的其实是一个Entry对象,key为当前的threadLocal对象,此对象继承了弱引用的类。

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

            Entry(ThreadLocal k, Object v) {
                //此处会把k包装成一个弱引用对象,也就是ThreadLocalMap的Key为弱引用对象
                super(k);
                value = v;
            }
        }

强引用:当对象还被使用的时候,JVM永远不会回收,宁愿抛出OOM;

软引用:当系统内存不足时,JVM的GC会回收这些对象信息,可以定义一些缓存信息,当内存不足,JVM回收,也只是缓存失效,并不一定影响系统功能(除非触发缓存雪崩);

弱引用:不管系统内存是否足够,GC都会回收;

虚引用:虚引用相当于没有引用;

那ThreadLocal存储的key为什么要用弱引用

1、作用就是在key不被使用的时候能被GC回收,防止OOM;

那么我们在使用ThreadLocal的时候,先存入,后面代码在使用,中间已经触发了GC,那么这时候对象是不是已经被GC回收了;

1、当然不是,如果这样就被回收,那这个对象其实毫无用处,因为JVM的GC一般由JVM控制,不是我们代码控制什么时候GC(当然System.gc()主动触发也可以);

2、我们在使用ThreadLocal对象的时候是 ThreadLocal threadLoacl=new ThreadLoal();这里进行了new一个对象出来,这时候的 threadLoacl是强引用,而ThreadlocalMap里面的弱引用key只是threadLocal对象的包装,其实质它还是强引用,只有threadLocal=null或者当前定义的对象为线程局部变量等,线程已经完成,threadLocal被JVM回收,它定义的key才真正成为了弱引用,这时候GC才会把它回收;

3、但我们为什么要把key设置成弱引用呢,这样可能引起OOM,假如不设置成弱引用,当我们设置threadLoacl=null时,我们希望它里面的Entry对象也被回收掉,但如果Entry是强引用的话,JVM就不会回收这个Entry对象了,而且这个对象以后都不可达,这样就很容易造成OOM;

那怎么处理key为null的value呢,它在set(),get(),remove()方法的时候,如果发现他的key为null,会对它对应value做处理,所以,如果要处理减少OOM,只需要在使用完成对象之前,根据实际业务调用下上面三个方法中的一个就可以了。

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.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 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;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

当定义过多的ThreadLocal会有什么坏处 

1、ThreadLocal对象每一个线程都是一个副本,高并发内存消耗过大(这也是ThreadLocal在高并发场景的一个作用空间换时间,synchronized等加锁问题正好相反,时间还空间); 

2、ThreadLocalMap虽然不是map,但它实现了map类似的功能,内部也使用了数组链表的存储方式,当出现了hash碰撞的时候,它会往下继续寻址,直到找到一个空的地址存放数据,这样当链表过长的时候,获取数据的时间也会更多,降低查询性能。

你可能感兴趣的:(JAVA记录)