ThreadLocal源码简解

一、ThreadLocal

ThreadLocal,线程副本变量。

ThreadLocal,保证了每个线程都有独立的对象副本,保证了对象的唯一性,可以实现线程安全性。

UML图

如下所示:

set(T value)

调用ThreadLocal的set(T value)和get()方法时,内部会使用到一个ThreadLocalMap。

/**
 * Sets the current thread's copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * override this method, relying solely on the {@link #initialValue}
 * method to set the values of thread-locals.
 *
 * @param value the value to be stored in the current thread's copy of
 *        this thread-local.
 */
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    //ThreadLocalMap对象map不存在,则创建map
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

get()

/**
 * Returns the value in the current thread's copy of this
 * thread-local variable.  If the variable has no value for the
 * current thread, it is first initialized to the value returned
 * by an invocation of the {@link #initialValue} method.
 *
 * @return the current thread's value of this thread-local
 */
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        //获取当前ThreadLocal对象对应的Entry节点
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

二、内部类ThreadLocalMap

ThreadLocalMap是ThreadLocal的一个内部静态类。

/**
 * Construct a new map initially containing (firstKey, firstValue).
 * ThreadLocalMaps are constructed lazily, so we only create
 * one when we have at least one entry to put in it.
 */
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}

getEntry()

ThreadLocalMap,是由多个节点(Entry)组成数组Entry[],通过Hash操作确定节点落在数组的哪个下标。

getEntry(ThreadLocal<?> key),可以通过相应的ThreadLocal对象,找到对应的Entry节点。

/**
 * Get the entry associated with key.  This method
 * itself handles only the fast path: a direct hit of existing
 * key. It otherwise relays to getEntryAfterMiss.  This is
 * designed to maximize performance for direct hits, in part
 * by making this method readily inlinable.
 *
 * @param  key the thread local object
 * @return the entry associated with key, or null if no such
 */
private Entry getEntry(ThreadLocal<?> key) {
    //寻找ThreadLocal对象作为key时,对应的下标
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    //当节点的key与对应的ThreadLocal对象相同时,说明是对应的节点。
    if (e != null && e.get() == key)
        return e;
    else
        //找不到则继续向下一个下标遍历。
        return getEntryAfterMiss(key, i, e);
}

三、Entry节点以及软引用

Entry类,构造方法是键值对,key为当前的ThreadLocal对象,而value就是副本变量。

节点Entry类,继承自软引用(WeakReference)。

软引用(WeakReference):当JVM认定内存空间不足时,垃圾回收器会去回收软引用指向的对象。JVM会确保在抛出OOM前清理软引用指向的对象。

/**
 * The entries in this hash map extend WeakReference, using
 * its main ref field as the key (which is always a
 * ThreadLocal object).  Note that null keys (i.e. entry.get()
 * == null) mean that the key is no longer referenced, so the
 * entry can be expunged from table.  Such entries are referred to
 * as "stale entries" in the code that follows.
 */
static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

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

四、内存泄露

内存泄漏(Memory Leak):是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

如果使用完ThreadLocal,没有用remove()清除掉无用的对象,可能会导致内存泄露。

/**
 * Remove the entry for key.
 */
private void remove(ThreadLocal<?> key) {
    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)]) {
        if (e.get() == key) {
            e.clear();
            expungeStaleEntry(i);
            return;
        }
    }
}

此处调用了Reference的clear()方法,如下:

/**
 * Clears this reference object.  Invoking this method will not cause this
 * object to be enqueued.
 *
 * <p> This method is invoked only by Java code; when the garbage collector
 * clears references it does so directly, without invoking this method.
 */
public void clear() {
    this.referent = null;
}

猜你喜欢

转载自www.cnblogs.com/expiator/p/12191625.html
今日推荐