ThreadLocal和强软弱虚

强软弱虚

强引用(StrongReference) : 最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Object obj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。

软引用(SoftReference) :在系统将要发生内存溢出之前,将会把这些对象列入回收范围之中进行第二次回收。如果这次回收后还没有足够的内存,才会抛出内存溢出异常。

0bject obj = new Object(); //声明强引用
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null; //销毁强引用

弱引用(WeakReference) :被弱引用关联的对象只能生存到下一次垃圾收集之前。当垃圾收集器工作时,无论内存空间是否足够,都会回收掉被弱引用关联的对象。

object obj = new Object(); //声明强引用
WeakReference<object> wr = new WeakReference<object>(obj ) ;
obj = null; //销毁强引用

虚引用(PhantomReference) :一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获得一个对象的实例。为-一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

ThreadLocal

set()方法

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

当调用set方法时,会将这个ThreadLocal对象作为key,传入的值作为value,放进一个map里面去,这个map通过getMap方法获取到,在getMap方法里面返回的是当前线程对应的一个Map,里面的threadLocals我们可以看到是Thread类里面的变量,源码如下:

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

threadlocals对应在Thread里面的位置如下:(与此线程有关的ThreadLocal值。该映射由ThreadLocal类维护)

 

 

回到set方法,里面调用map.set(this, value)方法的源码如下:

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

createMap()方法

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
}

的threadLocals继承于Thread类,而ThreadLocalMap的key就是在set方法里面的Entry,继承自WeakReference

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

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

内部结构

为什么Entry要使用弱引用?

若是强引用,即使tl=null, 但key 的引用依然指向ThreadLocal对象,即:当ThreadLocal不再使用需要回收时,发现某个线程中ThreadLocalMap存在该ThreadLocal的强引用,无法回收,造成内存泄漏所以会有内存泄露,而使用弱引用则不会;

但还是有内存泄漏存在,ThreadLocal被回收,key的值 变成null,则导致整个value再也无法被访问到,因此依然存在内存泄漏,因此要加一个remove方法,在每次get和set的时候都会清除掉key为null的数据。

应用

Spring里面如何保证执行的都是同一个数据库连接?

就是用的ThreadLocal,每个线程调用保证都是同一个连接

 

posted @ 2020-11-04 17:34  Mistolte  阅读(174)  评论(0)    收藏  举报