ThreadLocale理解和对WeakReference的运用

 理论基础看其他收录的文章,这里记录的是自己的理解。

 每个Thread都有一个成员变量ThreadLocal.ThreadLocalMap threadLocals = null;默认为空。

 ThreadLocale中的get()方法:
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}

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

getMap()方法获取了当前线程的threadLocals变量,是一个ThreadLocalMap对象,默认为空。ThreadLocale.set()的时候,才会对threadLocales赋值。

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
 可以说,Thread的threadLocals是专门为ThreadLocal而存在的。每次调用TheadLocal.get()的时候,其实是以ThreadLocal对象为key,每个使用到同一个ThreadLocal的线程拥有相同的ThreadLocal引用,但因为每个thread维护自己的threadLocals对象,所以,取到的数据是不同的,是线程私有的。

 以前理解的,是通过当前thread为key,取对应的值的想法是错误的。

 为什么这么设计呢?个人理解,如果使用一个map来存储thread和value的键值对。一个应用程序得有无穷尽个线程,而map的值又不会因为线程的销毁而销毁,很快就会因为thread过多,而又无法释放资源而耗尽内存。每个线程维护一个ThreadLocalMap对象,每次线程销毁的时候,值跟着就销毁了,不会存在内存耗尽的情况。
 再深入的想想,并不一定非得在每个Thread对象中拥有一个threadLocals变量。如果ThreadLocal中有个容器,使用WeakReference来保存thread和value的键值对,并不定时的清空weakreference.get()==null的键值对。应该也能实现同样的同能。但是复杂了很多。


 下面说说ThreadLocal对WeakReference的使用。

 Thread的threadLocals变量是一个ThreadLocalMap对象,是ThreadLocal的内部类。ThreadLocalMap不是一个map对象,只是取名为map而已。通过table保存Entity对象,从而维护值。
 static class Entry extends WeakReference {
     /** The value associated with this ThreadLocal. */
     Object value;

     Entry(ThreadLocal k, Object v) {
         super(k);
         value = v;
     }
 }
 private Entry[] table;

 Entity继承WeakReference,并且弱引用了key,即ThreadLocal对象。意味着,如果没有外部变量引用ThreadLocal对象,那么entity对象中的key将为null,value值不会被虚拟机销毁。这就是以下代码存在的原因:
private Entry getEntry(ThreadLocal key) {
        int i = key.threadLocalHashCode & (table.length - 1);
        Entry e = table[i];
        if (e != null && e.get() == key)
            return e;
        else
            return getEntryAfterMiss(key, i, e);
}

private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {
        Entry[] tab = table;
        int len = tab.length;

    while (e != null) {
        ThreadLocal k = e.get();
        if (k == key)
            return e;
        if (k == null)
            expungeStaleEntry(i);
        else
            i = nextIndex(i, len);
        e = tab[i];
    }
    return null;
}
   以前我以为,只要是被WeakReference引用的对象,在下一次gc()的时候就会被回收掉,这是不对的。还少了一个条件,就是WeakReference引用的对象不再被其他外部对象引用的时候,那么,WeakReference引用的对象在下一次gc()的时候就会被回收掉。

 看看这段代码,ThreadLocal的经典实用方式,但这里面有一些以前没考虑到的地方。
private static final ThreadLocal threadSession = new ThreadLocal();

     public static Session getSession() throws InfrastructureException {
          Session s = (Session) threadSession.get();
               try {
        if (s == null) {
            s = getSessionFactory().openSession();
            threadSession.set(s);
        }
    } catch (HibernateException ex) {
        throw new InfrastructureException(ex);
    }
    return s;
}
     }
 threadSession是一个类的静态成员变量,就是说threadSession在程序运行过程中,不会存在不被引用的情况,所以即使threadSession被WeakReference引用,但绝对不会被gc()销毁。因此不用考虑ThreadLocalMap的Entity的key==null的情况。

 为什么ThreadLocalMap不是map?
 个人理解,如果ThreadLocalMap是map,那么怎么让虚拟机销毁不再使用资源?虽然可以通过其他的技术手段来销毁,但毕竟增加了工作量,而jdk提供这么好用的WeakReference,为什么不使用呢?而且ThreadLocalMap是一个内部类,很明显,就是为了ThreadLocel而设计的。

你可能感兴趣的:(java基础)